<repo_name>jochumb/screenplay-pattern-test<file_sep>/src/test/java/nl/jochumborger/test/web/questions/TheCurrentUser.java
package nl.jochumborger.test.web.questions;

import net.serenitybdd.screenplay.Question;

/**
 * Created by jochum on 08/07/16.
 */
public class TheCurrentUser {

    public static Question<String> username() { return new Username(); }
    public static Question<String> twitterHandle() { return new TwitterHandle(); }

}
<file_sep>/src/test/java/nl/jochumborger/test/features/login/LoginStory.java
package nl.jochumborger.test.features.login;

import net.serenitybdd.junit.runners.SerenityRunner;
import net.serenitybdd.screenplay.Actor;
import net.serenitybdd.screenplay.abilities.BrowseTheWeb;
import net.thucydides.core.annotations.Managed;
import net.thucydides.core.annotations.Steps;
import nl.jochumborger.test.twitter.User;
import nl.jochumborger.test.web.questions.TheCurrentUser;
import nl.jochumborger.test.web.tasks.Login;
import nl.jochumborger.test.web.tasks.Logout;
import nl.jochumborger.test.web.tasks.OpenTwitter;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;

import static net.serenitybdd.screenplay.EventualConsequence.eventually;
import static net.serenitybdd.screenplay.GivenWhenThen.*;
import static org.hamcrest.Matchers.containsString;

/**
 * Created by jochum on 08/07/16.
 */
@RunWith(SerenityRunner.class)
public class LoginStory {

    User twitterUser = User.getInstance();

    Actor lexie = Actor.named("Lexie");

    @Managed(uniqueSession = true)
    public WebDriver herBrowser;

    @Steps
    OpenTwitter openTwitter;

    @Steps
    Logout logout;

    @Before
    public void lexieCanBrowseTheWeb() {
        lexie.can(BrowseTheWeb.with(herBrowser));
    }

    @Test
    public void log_in_with_correct_credentials_should_show_twitter_feed() {
        givenThat(lexie).wasAbleTo(openTwitter);

        when(lexie).attemptsTo(Login.withCredentials(twitterUser.getUsername(),twitterUser.getPassword()));

        then(lexie).should(
                eventually(seeThat(TheCurrentUser.username(), containsString(twitterUser.getDisplayName()))),
                seeThat(TheCurrentUser.twitterHandle(), containsString(twitterUser.getHandle()))
        );
    }

    @After
    public void logout() {
        lexie.attemptsTo(logout);
    }

}
<file_sep>/src/test/java/nl/jochumborger/test/web/questions/TwitterHandle.java
package nl.jochumborger.test.web.questions;

import net.serenitybdd.screenplay.Actor;
import net.serenitybdd.screenplay.Question;
import net.serenitybdd.screenplay.questions.Text;
import nl.jochumborger.test.web.ui.UserInformation;

/**
 * Created by jochum on 08/07/16.
 */
public class TwitterHandle implements Question<String> {

    @Override
    public String answeredBy(Actor actor) {
        return Text.of(UserInformation.TWITTER_HANDLE).viewedBy(actor).asString();
    }
}
<file_sep>/src/test/java/nl/jochumborger/test/web/ui/UserInformation.java
package nl.jochumborger.test.web.ui;

import net.serenitybdd.screenplay.targets.Target;
import org.openqa.selenium.By;

/**
 * Created by jochum on 08/07/16.
 */
public class UserInformation {
    public static Target USERNAME = Target.the("twitter username").located(By.cssSelector(".DashboardProfileCard-name a"));
    public static Target TWITTER_HANDLE = Target.the("twitter handle").located(By.cssSelector(".DashboardProfileCard-screenname a span"));
}
<file_sep>/src/test/java/nl/jochumborger/test/web/ui/UserMenu.java
package nl.jochumborger.test.web.ui;

import net.serenitybdd.screenplay.targets.Target;
import org.openqa.selenium.By;

/**
 * Created by jochum on 08/07/16.
 */
public class UserMenu {
    public static Target MENU_DROPDOWN = Target.the("menu dropdown button").located(By.cssSelector("a.dropdown-toggle"));
    public static Target LOGOUT = Target.the("logout link").located(By.cssSelector("#signout-button button"));
}
<file_sep>/src/test/java/nl/jochumborger/test/twitter/User.java
package nl.jochumborger.test.twitter;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * Created by jochum on 11/07/16.
 */
public class User {

    private Properties props;

    private static User instance;

    private User() {
        this.props = new Properties();
        try {
            InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("twitter.properties");
            this.props.load(in);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static User getInstance() {
        if (instance == null) {
            instance = new User();
        }
        return instance;
    }

    public String getUsername() {
        return props.getProperty("twitter.username");
    }

    public String getPassword() {
        return props.getProperty("twitter.password");
    }

    public String getDisplayName() {
        return props.getProperty("twitter.displayname");
    }

    public String getHandle() {
        return props.getProperty("twitter.handle");
    }

}
<file_sep>/src/test/java/nl/jochumborger/test/features/search/SearchStory.java
package nl.jochumborger.test.features.search;

import net.serenitybdd.junit.runners.SerenityRunner;
import net.serenitybdd.screenplay.Actor;
import net.thucydides.core.annotations.Steps;
import nl.jochumborger.test.api.abilities.UseTheApi;
import nl.jochumborger.test.api.tasks.ConnectToTwitter;
import nl.jochumborger.test.api.questions.NumberOfResults;
import nl.jochumborger.test.api.tasks.Search;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static net.serenitybdd.screenplay.GivenWhenThen.*;
import static org.hamcrest.Matchers.greaterThan;

/**
 * Created by jochum on 08/07/16.
 */
@RunWith(SerenityRunner.class)
public class SearchStory {

    Actor lexie = Actor.named("Lexie");

    @Steps
    ConnectToTwitter connectToTwitter;

    @Before
    public void lexieCanUseTheApi() {
        lexie.can(UseTheApi.withDefaultContext());
    }

    @Test
    public void search_twitter_via_api_should_contain_expected_results() {
        givenThat(lexie).wasAbleTo(connectToTwitter);
        when(lexie).attemptsTo(Search.forText("#test"));
        then(lexie).should(seeThat(NumberOfResults.count(), greaterThan(0))); //assumption that "#test" is regularly used
    }

}
<file_sep>/src/test/java/nl/jochumborger/test/api/exceptions/ActorCannotUseTheApiException.java
package nl.jochumborger.test.api.exceptions;

/**
 * Created by jochum on 11/07/16.
 */
public class ActorCannotUseTheApiException extends RuntimeException {
    public ActorCannotUseTheApiException(String name) {
        super(name);
    }
}
<file_sep>/README.md
# Screenplay Pattern test
Experimenting with Screenplay pattern in Serenity, testing simple Twitter functionality

## Project setup
The features folder contrains two stories implemented using the screenplay pattern.

1. Login
  * Uses the serenity-screenplay-webdriver module, has most of seleniums features covered
  * Classes in the web directory
2. Search
  * Uses the twitter API and the serenity-screenplay module, needs some code to bind the two
  * Classes in the api directory

## Configure
Rename twitter.properties.example and twitter4j.properties.example (delete .example) and fill with valid values.

## Run
mvn verify
