All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.serenitybdd.screenplay.Actor Maven / Gradle / Ivy
package net.serenitybdd.screenplay;
import net.serenitybdd.model.PendingStepException;
import net.serenitybdd.core.Serenity;
import net.serenitybdd.core.SkipNested;
import net.serenitybdd.core.eventbus.Broadcaster;
import net.serenitybdd.core.exceptions.IgnoreStepException;
import net.serenitybdd.core.parallel.Agent;
import net.serenitybdd.markers.IsHidden;
import net.serenitybdd.screenplay.events.*;
import net.serenitybdd.screenplay.facts.Fact;
import net.serenitybdd.screenplay.facts.FactLifecycleListener;
import net.serenitybdd.annotations.Pending;
import net.serenitybdd.annotations.Step;
import net.thucydides.model.environment.SystemEnvironmentVariables;
import net.thucydides.model.screenshots.ScreenshotAndHtmlSource;
import net.thucydides.model.steps.ExecutedStepDescription;
import net.thucydides.core.steps.StepEventBus;
import net.thucydides.core.steps.events.StepFinishedEvent;
import net.thucydides.core.steps.events.StepPendingEvent;
import net.thucydides.core.steps.events.StepStartedEvent;
import net.thucydides.core.steps.session.TestSession;
import net.thucydides.model.util.EnvironmentVariables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
import static net.serenitybdd.screenplay.Actor.ErrorHandlingMode.IGNORE_EXCEPTIONS;
import static net.serenitybdd.screenplay.Actor.ErrorHandlingMode.THROW_EXCEPTION_ON_FAILURE;
import static net.serenitybdd.screenplay.SilentTasks.isNestedInSilentTask;
import static net.serenitybdd.screenplay.SilentTasks.isSilent;
import static net.thucydides.model.ThucydidesSystemProperty.MANUAL_TASK_INSTRUMENTATION;
/**
* An actor represents the person or system using the application under test.
* Actors can have Abilities, which allows them to perform Tasks and Interactions.
* Actors can ask Questions about the state of the system.
*/
public class Actor implements PerformsTasks, SkipNested, Agent {
private String id;
private String name;
private final PerformedTaskTally taskTally = new PerformedTaskTally();
private final EventBusInterface eventBusInterface = new EventBusInterface();
private final ConsequenceListener consequenceListener = new ConsequenceListener(eventBusInterface);
private String description;
private final Map notepad = new HashMap<>();
private final Map abilities = new HashMap<>();
private String preferredPronoun;
public Actor(String name) {
this.name = name;
}
/**
* Add all the remembered items for the current actor to the other actor's memory
*
* @param otherActor
*/
public void brief(Actor otherActor) {
otherActor.notepad.putAll(this.notepad);
}
public String toString() {
return getNameOrPronoun();
}
/**
* Create a new actor with a given name.
* This actor will have no abilities initially, so you will need to assign some abilities
* using the whoCan() method.
*/
public static Actor named(String name) {
return new Actor(name);
}
public Actor describedAs(String description) {
this.description = description;
assignDescriptionToActor(description);
return this;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getNameOrPronoun() {
return (preferredPronoun != null) ? preferredPronoun : name;
}
public Actor can(T doSomething) {
if (doSomething instanceof RefersToActor) {
((RefersToActor) doSomething).asActor(this);
}
abilities.put(doSomething.getClass(), doSomething);
eventBusInterface.assignAbilityToActor(this, doSomething.toString());
return this;
}
/**
* Assign an ability to an actor.
*/
public Actor whoCan(T doSomething) {
return can(doSomething);
}
@SuppressWarnings("unchecked")
public T abilityTo(Class extends T> doSomething) {
T ability = (T) abilities.get(doSomething);
if (ability == null) {
ability = this.getAbilityThatExtends(doSomething);
}
return ability;
}
/**
* Return an ability that extends the given class. Can be a Superclass or an Interface. If there are multiple
* candidate Abilities, the first one found will be returned.
*
* @param extendedClass the Interface class that we expect to find
* @param the matching Ability cast to extendedClass or null if none match
*/
@SuppressWarnings("unchecked")
public C getAbilityThatExtends(Class extendedClass) {
// See if any ability extends doSomething
for (Map.Entry entry : abilities.entrySet()) {
// Return the first matching Ability we find
if (extendedClass.isAssignableFrom(entry.getKey())) {
return (C) entry.getValue();
}
}
return null;
}
/**
* Return a list of all {@link Ability}s which implement {@link HasTeardown}
*/
public List getTeardowns() {
List teardowns = new ArrayList<>();
for (Ability a : abilities.values()) {
if (a instanceof HasTeardown) {
teardowns.add((HasTeardown) a);
}
}
return teardowns;
}
/**
* A more readable way to access an actor's abilities.
*/
public T usingAbilityTo(Class extends T> doSomething) {
return abilityTo(doSomething);
}
/**
* A method used to declare that an actor is now the actor in the spotlight, without having them perform any tasks.
*/
public final void entersTheScene() {
}
public final void has(Performable... todos) {
attemptsTo(todos);
}
private final List factListeners = new ArrayList<>();
public final void has(Fact... facts) {
Arrays.stream(facts).forEach(
fact -> {
fact.setup(this);
eventBusInterface.assignFactToActor(this, fact.toString());
FactLifecycleListener listener = new FactLifecycleListener(this, fact);
factListeners.add(listener);
StepEventBus.getParallelEventBus().registerListener(listener);
}
);
}
/**
* A tense-neutral synonym for addFact() for use with given() clauses
*/
public final void wasAbleTo(Performable... todos) {
attemptsTo(todos);
}
public enum ErrorHandlingMode {
THROW_EXCEPTION_ON_FAILURE, IGNORE_EXCEPTIONS
}
public final void attemptsTo(ErrorHandlingMode mode, Performable... tasks) {
// Do not perform tasks if the test is being run in dry-run mode (e.g. for manual tests)
if (StepEventBus.getParallelEventBus().isDryRun()) {
return;
}
beginPerformance();
for (Performable task : tasks) {
if (isNestedInSilentTask()) {
performSilently(task);
} else if (isSilent(task)) {
performSilently(task);
} else if (isHidden(task) || shouldNotReport(task)) {
performWithoutReporting(task);
} else {
perform(InstrumentedTask.of(task));
}
}
endPerformance(mode);
}
public final void attemptsTo(Performable... tasks) {
attemptsTo(THROW_EXCEPTION_ON_FAILURE, tasks);
}
private boolean isHidden(Performable task) {
return task instanceof IsHidden;
}
private boolean shouldNotReport(Performable task) {
if (manualTaskInstrumentation() && noStepAnnotationIsPresentIn(task)) {
return true;
}
return !InstrumentedTask.isInstrumented(task) && !InstrumentedTask.shouldInstrument(task);
}
private boolean noStepAnnotationIsPresentIn(Performable task) {
try {
return task.getClass().getMethod("performAs").getAnnotation(Step.class) != null;
} catch (NoSuchMethodException e) {
return false;
}
}
@Override
public ANSWER asksFor(Question question) {
beginPerformance();
ANSWER answer = question.answeredBy(this);
endPerformance();
return answer;
}
private void performSilently(T todo) {
perform(todo);
}
private void performWithoutReporting(T todo) {
perform(todo);
}
private void performConditionally(T todo) {
perform(todo);
}
private void perform(T todo) {
if (isPending(todo)) {
stepPending();
}
try {
notifyPerformanceOf(todo);
taskTally.newTask();
performTask(todo);
// TODO: When and how should this work
if (anOutOfStepErrorOccurred()) {
eventBusInterface.mergePreviousStep();
}
} catch (Throwable exception) {
if (!pendingOrIgnore(exception) && !thisIsAnExceptionBubblingUpFromAPreviousFailure()) {
eventBusInterface.reportStepFailureFor(todo, exception);
}
if (Serenity.shouldThrowErrorsImmediately() || isAnAssumptionFailure(exception)) {
throw exception;
}
} finally {
eventBusInterface.updateOverallResult();
}
}
private static boolean thisIsAnExceptionBubblingUpFromAPreviousFailure() {
return StepEventBus.getParallelEventBus().getBaseStepListener().aStepHasFailed();
}
private void performTask(T todo) {
if (!StepEventBus.getParallelEventBus().currentTestIsSuspended()) {
EventBusInterface.castActor(name);
todo.performAs(this);
}
}
private void notifyPerformanceOf(T todo) {
Broadcaster.getEventBus().post(new ActorPerforms(todo, getName()));
}
private boolean isPending(T todo) {
Method performAs = getPerformAsForClass(todo.getClass().getSuperclass()).orElse(getPerformAsForClass(todo.getClass()).orElse(null));
return (performAs != null) && (performAs.getAnnotation(Pending.class) != null);
}
private Optional getPerformAsForClass(Class taskClass) {
try {
return Optional.of(taskClass.getMethod("performAs", Actor.class));
} catch (NoSuchMethodException e) {
return Optional.empty();
}
}
private boolean pendingOrIgnore(Throwable exception) {
return exception instanceof IgnoreStepException ||
exception instanceof PendingStepException;
}
private boolean isAnAssumptionFailure(Throwable e) {
return e.getClass().getSimpleName().contains("Assumption");
}
public final void can(Consequence>... consequences) {
should(consequences);
}
public final void should(String groupStepName, Consequence>... consequences) {
try {
String groupTitle = injectActorInto(groupStepName);
stepStarted(groupTitle);
should(consequences);
} catch (Throwable error) {
throw error;
} finally {
//StepEventBus.getParallelEventBus().stepFinished();
stepFinished();
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(Actor.class);
private void stepStarted(String groupTitle) {
if (!TestSession.isSessionStarted()) {
StepEventBus.getParallelEventBus().stepStarted(ExecutedStepDescription.withTitle(groupTitle));
} else {
TestSession.addEvent(new StepStartedEvent( ExecutedStepDescription.withTitle(groupTitle)));
}
}
private void stepFinished() {
if (TestSession.isSessionStarted()) {
List screenshotList = TestSession.getTestSessionContext().getStepEventBus().takeScreenshots();
TestSession.addEvent(new StepFinishedEvent(screenshotList));
} else {
StepEventBus.getParallelEventBus().stepFinished();
}
}
private void stepPending() {
if (TestSession.isSessionStarted()) {
TestSession.addEvent(new StepPendingEvent());
} else {
StepEventBus.getParallelEventBus().stepPending();
}
}
private String injectActorInto(String groupStepName) {
return groupStepName.replaceAll("\\{0\\}", this.toString());
}
public final void should(List> consequences) {
should(consequences.toArray(new Consequence[]{}));
}
public final void should(Consequence>... consequences) {
ErrorTally errorTally = new ErrorTally(eventBusInterface);
startConsequenceCheck();
for (Consequence> consequence : consequences) {
check(consequence, errorTally);
}
endConsequenceCheck();
errorTally.reportAnyErrors();
}
private boolean anOutOfStepErrorOccurred() {
if (!eventBusInterface.isBaseStepListenerRegistered()) {
return false;
}
if (eventBusInterface.aStepHasFailedInTheCurrentExample()) {
return (eventBusInterface.getRunningStepCount()) > taskTally.getPerformedTaskCount();
} else {
return false;
}
}
private void check(Consequence consequence, ErrorTally errorTally) {
ConsequenceCheckReporter reporter = new ConsequenceCheckReporter(eventBusInterface, consequence);
try {
reporter.startQuestion(this);
if (eventBusInterface.shouldIgnoreConsequences()) {
reporter.reportStepIgnored();
} else {
consequence.evaluateFor(this);
reporter.reportStepFinished();
}
} catch (IgnoreStepException e) {
reporter.reportStepIgnored();
} catch (Throwable e) {
errorTally.recordError(consequence, e);
}
}
public void remember(String key, Question question) {
beginPerformance();
ANSWER answer = this.asksFor(question);
notepad.put(key, answer);
endPerformance();
}
public void remember(String key, Object value) {
notepad.put(key, value);
}
@SuppressWarnings("unchecked")
public T recall(String key) {
return (T) notepad.get(key);
}
public Map recallAll() {
return new HashMap<>(notepad);
}
@SuppressWarnings("unchecked")
public T forget(String key) {
return (T) notepad.remove(key);
}
public T sawAsThe(String key) {
return recall(key);
}
public T gaveAsThe(String key) {
return recall(key);
}
private void beginPerformance() {
Serenity.setSessionVariable(Agent.IN_THE_CURRENT_SESSION).to(this);
Broadcaster.getEventBus().post(new ActorBeginsPerformanceEvent(name));
}
private void endPerformance() {
endPerformance(THROW_EXCEPTION_ON_FAILURE);
}
private void endPerformance(ErrorHandlingMode mode) {
Broadcaster.getEventBus().post(new ActorEndsPerformanceEvent(name));
boolean isAFixtureMethod = StepEventBus.getParallelEventBus().inFixtureMethod();
if (mode == THROW_EXCEPTION_ON_FAILURE && !isAFixtureMethod) {
eventBusInterface.failureCause().ifPresent(
cause -> {
StepEventBus.getParallelEventBus().notifyFailure();
//StepEventBus.getParallelEventBus().testFinished(StepEventBus.getParallelEventBus().currentTestOutcomeIsDataDriven());
if (cause.isCompromised()) {
throw cause.asCompromisedException();
} else if (cause.isAnError()) {
throw cause.asError();
} else if (cause.isAnAssertionError()) {
throw cause.asAssertionError();
} else if (cause.getOriginalCause() instanceof RuntimeException) {
throw (RuntimeException) cause.getOriginalCause();
} else {
throw cause.asFailure();
}
}
);
}
}
private void startConsequenceCheck() {
beginPerformance();
consequenceListener.beginConsequenceCheck();
Broadcaster.getEventBus().post(new ActorBeginsConsequenceCheckEvent(name));
}
private void endConsequenceCheck() {
consequenceListener.endConsequenceCheck();
Broadcaster.getEventBus().post(new ActorEndsConsequenceCheckEvent(name));
endPerformance(IGNORE_EXCEPTIONS);
}
public Actor usingPronoun(String pronoun) {
this.preferredPronoun = pronoun;
return this;
}
public Actor withNoPronoun() {
this.preferredPronoun = null;
return this;
}
public void assignDescriptionToActor(String description) {
StepEventBus.getParallelEventBus().getBaseStepListener().latestTestOutcome().ifPresent(
testOutcome -> testOutcome.assignDescriptionToActor(getName(), description)
);
}
public void assignName(String name) {
this.name = name;
}
private boolean manualTaskInstrumentation() {
EnvironmentVariables environmentVariables = SystemEnvironmentVariables.currentEnvironmentVariables();
return (MANUAL_TASK_INSTRUMENTATION.booleanFrom(environmentVariables, false));
}
public void wrapUp() {
getTeardowns().forEach(HasTeardown::tearDown);
factListeners.forEach(
factLifecycleListener -> StepEventBus.getParallelEventBus().dropListener(factLifecycleListener)
);
}
@Override
public String getId() {
return id;
}
}