All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seleniumtests.reporter.logger.TestStep Maven / Gradle / Ivy

There is a newer version: 4.23.18
Show newest version
/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 * 				Copyright 2017-2019 B.Hecquet
 *
 * 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 com.seleniumtests.reporter.logger;

import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.openqa.selenium.WebDriverException;
import org.testng.ITestResult;

import com.seleniumtests.core.Step.RootCause;
import com.seleniumtests.core.TestStepManager;
import com.seleniumtests.reporter.reporters.CommonReporter;
import com.seleniumtests.util.ExceptionUtility;

/**
 * Group of test actions
 * Represent each method call inside a PageObject during the test
 * TestStep allows to log any action done during the test. See {@link com.seleniumtests.core.aspects.LogAction#logTestStep }
 * We get a tree like this:
 * root (TestStep)
 * +--- action1 (TestAction)
 * +--+ sub-step1 (TestStep)
 *    +--- sub-action1
 *    +--- message (TestMessage)
 *    +--- sub-action2
 * +--- action2
 * 
 * Each TestStep is then logged in {@link TestLogging TestLogging class} with ScenarioLogger.logTestStep() method
 * 
 * @author behe
 *
 */
public class TestStep extends TestAction {
	private static final String BEFORE_STEP_PREFIX = "before-";
	private List stepActions;
	private Long duration;
	private Date startDate;
	private long videoTimeStamp;
	private List harCaptures;
	private List files;
	private List snapshots;
	private ITestResult testResult;
	private RootCause errorCause;
	private String errorCauseDetails;
	private Integer stepResultId;  // the stepResult if it has been recorded on seleniumRobot-server
	private boolean disableBugtracker;
	
	public enum StepStatus {
		SUCCESS,
		FAILED,
		WARNING
	}
	
	/**
	 * 
	 * @param name			action name
	 * @param testResult	associated TestNG result
	 * @param pwdToReplace	list of string to replace when returning actions so that passwords are masked
	 */
	public TestStep(String name, ITestResult testResult, List pwdToReplace, boolean maskPassword) {
		this(name, testResult, pwdToReplace, maskPassword, RootCause.NONE, null, false);
	}
	public TestStep(String name, ITestResult testResult, List pwdToReplace, boolean maskPassword, RootCause errorCause, String errorCauseDetails, boolean disableBugtracker) {
		super(name, false, pwdToReplace);
		this.maskPassword = maskPassword;
		stepActions = new ArrayList<>();
		files = new ArrayList<>();
		harCaptures = new ArrayList<>();
		snapshots = new ArrayList<>();
		duration = 0L;
		startDate = new Date();
		this.testResult = testResult;
		
		if (errorCause != RootCause.NONE) {
			this.errorCause = errorCause;
			this.errorCauseDetails = errorCauseDetails;
		}
		this.disableBugtracker = disableBugtracker;
	}
	
	public Long getDuration() {
		long consolidatedDuration = duration;
		consolidatedDuration -= getDurationToExclude();

		return Math.max(0, consolidatedDuration);
	}
	
	public boolean isTestEndStep() {
		return TestStepManager.LAST_STEP_NAME.equals(name);
	}
	
	public void setStartDate() {
		startDate = new Date();
	}
	
	public void setStartDate(Date date) {
		startDate = date;
	}
	
	@Override
	public long getDurationToExclude() {
		long consolidatedDurationToExclude = durationToExclude; 
		
		for (Snapshot snapshot: snapshots) {
			consolidatedDurationToExclude += snapshot.getDurationToExclude();
		}
		
		for (TestAction action: stepActions) {
			consolidatedDurationToExclude += action.getDurationToExclude();
		}
		
		return consolidatedDurationToExclude;
	}

	/**
	 * set duration in milliseconds
	 * @param duration
	 */
	public void setDuration(Long duration) {
		this.duration = duration;
	}
	
	public void updateDuration() {
		duration = new Date().getTime() - startDate.getTime();
		
	}

	public List getStepActions() {
		return stepActions;
	}

	/**
	 * Return 
	 * StepStatus.FAILED if and only if the step is marked as failed
	 * StepStatus.WARNIG if any of the sub actions (or steps) are failed, but not the step itself
	 * StepStatus.SUCESS in any other cases
	 */
	public StepStatus getStepStatus() {
		if (Boolean.TRUE.equals(getFailed())) {
			return StepStatus.FAILED;
		} 
		for (TestAction action: stepActions) {
			if ((action instanceof TestStep && ((TestStep) action).getStepStatus() != StepStatus.SUCCESS)
					|| Boolean.TRUE.equals(action.getFailed())) {
				return StepStatus.WARNING;
			}
			
		}
		return StepStatus.SUCCESS;
	}
	
	public String getExceptionMessage(String format) {
		if (actionException == null) {
			return "";
		}
		StringBuilder stackString = new StringBuilder();
		ExceptionUtility.generateTheStackTrace(actionException, actionException.getMessage(), stackString, format);
		return stackString.toString();
	}

	public void addAction(TestAction action) {
		action.setPosition(stepActions.size());
		stepActions.add(action);
		action.setParent(this);
		
		// add replacement of the parent to this action
		action.pwdToReplace.addAll(pwdToReplace);
		
		// inherit password masking from step
		action.maskPassword = maskPassword;
	}
	public void addMessage(TestMessage message) {
		message.setPosition(stepActions.size());
		stepActions.add(message);
		message.setParent(this);

		// add replacement of the parent to this message
		message.pwdToReplace.addAll(pwdToReplace);

		// inherit password masking from step
		message.maskPassword = maskPassword;
	}
	public void addValue(TestValue value) {
		value.setPosition(stepActions.size());
		stepActions.add(value);
		value.setParent(this);
	}
	public void addStep(TestStep step) {
		step.setPosition(stepActions.size());
		stepActions.add(step);
		step.setParent(this);
		
		// add replacement of the parent step to this step
		step.pwdToReplace.addAll(pwdToReplace);

		// inherit password masking from step
		step.maskPassword = maskPassword;
	}
	public void addNetworkCapture(HarCapture har) {
		har.setPosition(stepActions.size());
		harCaptures.add(har);
		har.setParent(this);
	}
	public void addFile(GenericFile file) {
		file.setPosition(stepActions.size());
		files.add(file);
		file.setParent(this);
	}
	
	/**
	 * Add snapshot to this step
	 * @param snapshot		the snapshot
	 * @param stepIdx		the index of this step in the test
	 * @param userGivenName name of the snapshot, user wants to display
	 */
	public void addSnapshot(Snapshot snapshot, int stepIdx, String userGivenName) {
		snapshot.setPosition(snapshots.size());
		
		// rename file so that user can easily consult it
		snapshot.rename(this, position, snapshot.position + getSnapshotIndex(), userGivenName);
		
		snapshots.add(snapshot);
		snapshot.setParent(this);
	}
	
	private int getSnapshotIndex() {
		return getSnapshotIndex(1) - position;
	}
	private int getSnapshotIndex(int multiplier) {
		int idx = (position + 1) * multiplier;
		if (getParent() instanceof TestStep) {
			idx += ((TestStep)getParent()).getSnapshotIndex(multiplier * 10);
		} 
		return idx;
	}
	
	@Override
	public String toString() {
		return toString(0);
	}
	
	@Override
	public String toString(int spaces) {
		String currentIndent = StringUtils.repeat(" ", spaces);
		StringBuilder testStepRepr = new StringBuilder(String.format("Step %s\n", getName()));
		for (TestAction testAction: getStepActions()) {
			testStepRepr.append(currentIndent + "  - " + testAction.toString(spaces + 2) + "\n");
		}
		
		return testStepRepr.toString().trim();
	}
	
	@Override
	public JSONObject toJson() {
		JSONObject stepJSon = new JSONObject();
		
		stepJSon.put("name", encodeString(getName(), "json"));
		stepJSon.put("type", "step");
		stepJSon.put("harCaptures", new JSONArray());
		for (HarCapture harCapture: getHarCaptures()) {
			stepJSon.getJSONArray("harCaptures").put(harCapture.toJson());
		}
		
		stepJSon.put("actions", new JSONArray());
		for (TestAction testAction: getStepActions()) {
			stepJSon.getJSONArray("actions").put(testAction.toJson());
		}
		
		stepJSon.put("files", new JSONArray());
		for (GenericFile file: getFiles()) {
			stepJSon.getJSONArray("files").put(file.toJson());
		}
		
		return stepJSon;
	}

	public Date getStartDate() {
		return startDate;
	}

	public List getSnapshots() {
		return snapshots;
	}

	public ITestResult getTestResult() {
		return testResult;
	}
	
	/**
	 * Returns the list of files referenced by this test step and sub-steps
	 * @return
	 */
	public List getAllAttachments() {
		return getAllAttachments(false);
	}
	public List getAllAttachments(boolean onlyPictureOfSnapshots) {
		List usedFiles = new ArrayList<>();
		for (Snapshot snapshot: snapshots) {
			if (snapshot == null || snapshot.getScreenshot() == null ) {
				continue;
			}
			
			if (snapshot.getScreenshot().getFullHtmlPath() != null && !onlyPictureOfSnapshots) {
				try {
					usedFiles.add(new File(snapshot.getScreenshot().getFullHtmlPath()).getCanonicalFile());
				} catch (IOException e) {
					// error while getting HtmlPath
				}
			}
			if (snapshot.getScreenshot().getFullImagePath() != null) {
				try {
					usedFiles.add(new File(snapshot.getScreenshot().getFullImagePath()).getCanonicalFile());
				} catch (IOException e) {
					// error getting image path
				}
			}
		}
		
		// add attachments declared in sub-steps
		for (TestAction subStep: stepActions.stream().filter(a -> a instanceof TestStep).collect(Collectors.toList())) {
			usedFiles.addAll(((TestStep)subStep).getAllAttachments(onlyPictureOfSnapshots));
		}
		
		usedFiles.addAll(getFiles().stream().map(GenericFile::getFile).collect(Collectors.toList()));
		usedFiles.addAll(getHarCaptures().stream().map(HarCapture::getFile).collect(Collectors.toList()));
		
		return usedFiles;
	}
	
	/**
	 * move attachments from "before-" folder to outputDirectory folder
	 * @param outputDirectory
	 * @throws IOException 
	 */
	public void moveAttachments(String outputDirectory) throws IOException {

		for (Snapshot snapshot: snapshots) {
			if (snapshot == null 
					|| snapshot.getScreenshot() == null 
					|| snapshot.getScreenshot().getFullHtmlPath() == null 
					|| !snapshot.getScreenshot().getFullHtmlPath().contains(BEFORE_STEP_PREFIX) ) {
				continue;
			}
			try {
				snapshot.relocate(outputDirectory);
			} catch (IOException e) {
				logger.error("Cannot relocate snapshot: " + e.getMessage());
			}
			
		}
	
		// move attachments in sub-steps
		for (TestAction subStep: stepActions.stream().filter(a -> a instanceof TestStep).collect(Collectors.toList())) {
			((TestStep)subStep).moveAttachments(outputDirectory);
		}
	
		for (HarCapture harCapture: harCaptures) {
			if (harCapture.getFile().toString().contains(BEFORE_STEP_PREFIX)) {
				harCapture.relocate(outputDirectory);
			}
		}
		
		for (GenericFile genericFile: files) {
			if (genericFile.getFile().toString().contains(BEFORE_STEP_PREFIX)) {
				genericFile.relocate(outputDirectory);
			}
		}
	}

	public List getHarCaptures() {
		return harCaptures;
	}

	public void setHarCaptures(List harCaptures) {
		this.harCaptures = harCaptures;
	}
	

	/**
	 * @deprecated use 'getRootCause' instead
	 * @return
	 */
	@Deprecated
	public RootCause getErrorCause() {
		return getRootCause();
	}

	public RootCause getRootCause() {
		if (getStepStatus() == StepStatus.SUCCESS) {
			return null;
		}
		if (errorCause != null) {
			return errorCause;
		} 
		for (TestAction action: stepActions) {
			if ((action instanceof TestStep && ((TestStep) action).getRootCause() != null)) {
				return ((TestStep) action).getRootCause();
			}
		}
		
		return errorCause;
	}
	

	/**
	 * @deprecated use getRootCauseDetails instead
	 * @return
	 */
	@Deprecated
	public String getErrorCauseDetails() {
		return getRootCauseDetails();
	}
	
	/**
	 * Returns the error cause detail of the step or any sub step
	 * @return
	 */
	public String getRootCauseDetails() {
		if (getStepStatus() == StepStatus.SUCCESS) {
			return null;
		}
		if (errorCause != null) {
			return errorCauseDetails;
		} 
		for (TestAction action: stepActions) {
			if ((action instanceof TestStep && ((TestStep) action).getRootCause() != null)) {
				return ((TestStep) action).getRootCauseDetails();
			}
		}
		return errorCauseDetails;
	}
	public boolean isDisableBugtracker() {
		return disableBugtracker;
	}
	public List getFiles() {
		return files;
	}

	/**
	 * Creates an new TestStep, encoding text of sub-steps and elements.
	 */
	@Override
	public TestStep encode(String format) {

		List encodedPasswords = encodePasswords(pwdToReplace, format);
		TestStep step = new TestStep(encodeString(name, format), testResult, encodedPasswords, maskPassword);
		
		step.stepActions = new ArrayList<>();
		for (TestAction testAction: stepActions) {
			step.stepActions.add(testAction.encode(format));
		}
		
		step.failed = failed;
		step.snapshots = new ArrayList<>(snapshots);
		step.files = new ArrayList<>();
		for (GenericFile file: files) {
			step.files.add(file.encode(format));
		}
		
		if (format == null) {
			step.encoded = encoded;
		} else {
			step.encoded = true;
		}
		
		step.duration = duration;
		step.startDate = startDate;
		step.videoTimeStamp = videoTimeStamp;
		step.harCaptures = new ArrayList<>();
		for (HarCapture har: harCaptures) {
			step.harCaptures.add(har);
		}
		step.actionException = actionException;
		if (actionException != null) {
			step.actionExceptionMessage = encodeString(ExceptionUtility.getExceptionMessage(actionException), format);
		}
		

		step.errorCause = errorCause;
		step.errorCauseDetails = encodeString(errorCauseDetails, format);
		step.disableBugtracker = disableBugtracker;
		
		return step;
	}

	@Override
	public TestStep deepCopy() {
		return encode(null);
	}
	public long getVideoTimeStamp() {
		return videoTimeStamp;
	}
	public void setVideoTimeStamp(long videoTimeStamp) {
		this.videoTimeStamp = videoTimeStamp;
	}
	public Integer getStepResultId() {
		return stepResultId;
	}
	public void setStepResultId(Integer stepResultId) {
		this.stepResultId = stepResultId;
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy