org.awesomeagile.webapp.AwesomeAgileFunctionalTest.java Source code

Java tutorial

Introduction

Here is the source code for org.awesomeagile.webapp.AwesomeAgileFunctionalTest.java

Source

package org.awesomeagile.webapp;

/*
 * ================================================================================================
 * Awesome Agile
 * %%
 * Copyright (C) 2015 Mark Warren, Phillip Heller, Matt Kubej, Linghong Chen, Stanislav Belov, Qanit Al
 * %%
 * 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.
 * ------------------------------------------------------------------------------------------------
 */

import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

import com.gargoylesoftware.htmlunit.BrowserVersion;

import org.apache.commons.lang3.StringUtils;
import org.awesomeagile.AwesomeAgileApplication;
import org.awesomeagile.dao.testing.TestDatabase;
import org.awesomeagile.integrations.hackpad.PadIdentity;
import org.awesomeagile.testing.google.FakeGoogleServer;
import org.awesomeagile.testing.google.Person;
import org.awesomeagile.testing.google.Person.Email;
import org.awesomeagile.testing.google.Person.Image;
import org.awesomeagile.testing.google.Person.Name;
import org.awesomeagile.testing.hackpad.FakeHackpadServer;
import org.awesomeagile.webapp.AwesomeAgileFunctionalTest.EnvInitializer;
import org.awesomeagile.webapp.page.HackpadPage;
import org.awesomeagile.webapp.page.LandingPage;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.support.PageFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

/**
 * An end-to-end test for the sign up flows.
 *
 * @author sbelov@google.com (Stan Belov)
 */
@RunWith(SpringJUnit4ClassRunner.class)
@WebIntegrationTest(randomPort = true)
@SpringApplicationConfiguration(classes = { AwesomeAgileApplication.class }, initializers = {
        EnvInitializer.class })
public class AwesomeAgileFunctionalTest {

    private static final String DATABASE_NAME = "awesomeagile";
    private static final String CLIENT_ID = "testclient-1.google.com";
    private static final String CLIENT_SECRET = "verystrongsecret";
    private static final String HACKPAD_CLIENT_ID = "hackpadclient";
    private static final String HACKPAD_CLIENT_SECRET = "hackpadsecret";
    private static final String DISPLAY_NAME = "sbelov";
    private static final String DEFINITION_OF_READY_TEMPLATE_ID = "Defnready-xyz";
    private static final String DEFINITION_OF_READY_CONTENTS = "This is the definition of ready";
    private static final String DEFINITION_OF_DONE_TEMPLATE_ID = "Defndone-xyz";
    private static final String DEFINITION_OF_DONE_CONTENTS = "This is the definition of done";
    private static final AtomicLong idProvider = new AtomicLong(1);

    public static final class EnvInitializer
            implements ApplicationContextInitializer<ConfigurableApplicationContext> {

        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.getEnvironment().getPropertySources()
                    .addFirst(new MapPropertySource("overrides",
                            new ImmutableMap.Builder<String, Object>()
                                    .put("spring.datasource.url", testDatabase.getUrl(DATABASE_NAME))
                                    .put("spring.datasource.username", testDatabase.getUserName())
                                    .put("spring.datasource.password", testDatabase.getPassword())
                                    .put("spring.social.google.api.url", fakeGoogleServer.getEndpoint())
                                    .put("spring.social.google.oauth.url", fakeGoogleServer.getEndpoint())
                                    .put("spring.social.google.clientId", CLIENT_ID)
                                    .put("spring.social.google.secret", CLIENT_SECRET)
                                    .put("spring.social.google.scope", "profile email")
                                    .put("hackpad.client.id", HACKPAD_CLIENT_ID)
                                    .put("hackpad.client.secret", HACKPAD_CLIENT_SECRET)
                                    .put("hackpad.url", fakeHackpadServer.getEndpoint())
                                    .put("hackpad.templates.defnready", DEFINITION_OF_READY_TEMPLATE_ID)
                                    .put("hackpad.templates.defndone", DEFINITION_OF_DONE_TEMPLATE_ID).build()));
        }
    }

    @ClassRule
    public static TestDatabase testDatabase = new TestDatabase(DATABASE_NAME);

    @ClassRule
    public static FakeGoogleServer fakeGoogleServer = new FakeGoogleServer();

    @ClassRule
    public static FakeHackpadServer fakeHackpadServer = new FakeHackpadServer();

    @Value("${local.server.port}")
    private int port;

    private HtmlUnitDriver driver;

    @Before
    public void setUp() throws Exception {
        System.out.println("Fake Google OAuth2 server up at: " + fakeGoogleServer.getEndpoint());
        System.out.println("Fake Hackpad server up at: " + fakeHackpadServer.getEndpoint());
        System.out.println("AwesomeAgile web application up at: " + getEndpoint());
        fakeGoogleServer.setClientId(CLIENT_ID);
        fakeGoogleServer.setClientSecret(CLIENT_SECRET);
        fakeGoogleServer.setRedirectUriPrefixes(ImmutableList.of("http://localhost:" + port + "/"));
        fakeGoogleServer.setPerson(createUser());
        fakeHackpadServer.setClientId(HACKPAD_CLIENT_ID);
        fakeHackpadServer.setClientSecret(HACKPAD_CLIENT_SECRET);
        fakeHackpadServer.getHackpads().clear();
        fakeHackpadServer.addHackpad(new PadIdentity(DEFINITION_OF_READY_TEMPLATE_ID),
                DEFINITION_OF_READY_CONTENTS);
        fakeHackpadServer.addHackpad(new PadIdentity(DEFINITION_OF_DONE_TEMPLATE_ID), DEFINITION_OF_DONE_CONTENTS);

        driver = new HtmlUnitDriver(BrowserVersion.CHROME);
        driver.setJavascriptEnabled(true);
    }

    private Person createUser() {
        long userId = idProvider.getAndIncrement();
        return new Person().setId(String.valueOf(userId)).setDisplayName(DISPLAY_NAME)
                .setName(new Name("Stan", "Belov"))
                .setEmails(ImmutableList.of(new Email(emailForUser(userId), "account")))
                .setImage(new Image("http://static.google.com/avatar.jpg"));
    }

    private static String emailForUser(long id) {
        return String.format("user%03d@gmail.com", id);
    }

    /**
     * Verifies the normal sign up flow:
     * 1. User clicks on the login button.
     * 2. User selects Google as an OAuth2 provider.
     * 3. Google (fake Google OAuth server in this case) authenticates the user
     * and redirects back to the web application with the code request parameter set.
     * 4. Web application requests an OAuth2 token based on the code it received.
     * 5. User is logged in, and his display name is visible on the landing page.
     *
     * @throws Exception
     */
    @Test
    public void testSignupFlow() throws Exception {
        LandingPage landingPage = PageFactory.initElements(driver, LandingPage.class);
        landingPage.loginWithGoogle(getEndpoint());
        assertEquals(DISPLAY_NAME, landingPage.getUserName());
        assertFalse(landingPage.isLoginButtonVisible());
    }

    @Test
    public void testCreateDefinitionOfReady() throws Exception {
        assertEquals(2, fakeHackpadServer.getHackpads().size());
        LandingPage landingPage = PageFactory.initElements(driver, LandingPage.class);
        landingPage.loginWithGoogle(getEndpoint());
        assertThat(driver.getWindowHandles(), hasSize(1));
        String firstWindow = driver.getWindowHandle();
        landingPage.createDefinitionOfReady();
        assertThat(driver.getWindowHandles(), hasSize(2));
        assertEquals(3, fakeHackpadServer.getHackpads().size());

        String newWindow = Iterables
                .getFirst(Sets.difference(driver.getWindowHandles(), ImmutableSet.of(firstWindow)), null);
        driver.switchTo().window(newWindow);
        HackpadPage hackpadPage = PageFactory.initElements(driver, HackpadPage.class);
        assertEquals(DEFINITION_OF_READY_CONTENTS, hackpadPage.getContent());
        String newHackpadUrl = driver.getCurrentUrl();
        String newHackpadId = StringUtils.substringAfterLast(newHackpadUrl, "/");
        assertNotEquals(DEFINITION_OF_READY_TEMPLATE_ID, newHackpadId);
        String newHackpad = fakeHackpadServer.getHackpad(new PadIdentity(newHackpadId));
        assertEquals(DEFINITION_OF_READY_CONTENTS, newHackpad);
    }

    @Test
    public void testCreateDefinitionOfDone() throws Exception {
        assertEquals(2, fakeHackpadServer.getHackpads().size());
        LandingPage landingPage = PageFactory.initElements(driver, LandingPage.class);
        landingPage.loginWithGoogle(getEndpoint());
        assertThat(driver.getWindowHandles(), hasSize(1));
        String firstWindow = driver.getWindowHandle();
        landingPage.createDefinitionOfDone();
        assertThat(driver.getWindowHandles(), hasSize(2));
        assertEquals(3, fakeHackpadServer.getHackpads().size());

        String newWindow = Iterables
                .getFirst(Sets.difference(driver.getWindowHandles(), ImmutableSet.of(firstWindow)), null);
        driver.switchTo().window(newWindow);
        HackpadPage hackpadPage = PageFactory.initElements(driver, HackpadPage.class);
        assertEquals(DEFINITION_OF_DONE_CONTENTS, hackpadPage.getContent());
        String newHackpadUrl = driver.getCurrentUrl();
        String newHackpadId = StringUtils.substringAfterLast(newHackpadUrl, "/");
        assertNotEquals(DEFINITION_OF_DONE_TEMPLATE_ID, newHackpadId);
        String newHackpad = fakeHackpadServer.getHackpad(new PadIdentity(newHackpadId));
        assertEquals(DEFINITION_OF_DONE_CONTENTS, newHackpad);
    }

    /**
     * Tests the fact that dashboard view can retrieve links
     * to the previously created Hackpad documents with the following steps:
     * 1. Log in.
     * 2. Click on create definition of ready button and wait until the view button is visible.
     * 3. Refresh Awesome Agile window.
     * 4. Verify that the view button is visible, and create button is invisible.
     * 5. Click on the view button and verify the contents.
     * @throws Exception
     */
    @Test
    public void testDashboard() throws Exception {
        LandingPage landingPage = PageFactory.initElements(driver, LandingPage.class);
        landingPage.loginWithGoogle(getEndpoint());
        assertThat(driver.getWindowHandles(), hasSize(1));
        String firstWindow = driver.getWindowHandle();
        landingPage.createDefinitionOfReady();
        closeWindowsExceptFor(driver, firstWindow);
        assertEquals(1, driver.getWindowHandles().size());
        driver.switchTo().window(firstWindow);
        driver.navigate().refresh();
        landingPage.waitForDefinitionOfReady();
        assertTrue(landingPage.isDefinitionOfReadyViewable());
        landingPage.viewDefinitionOfReady();
        String newWindow = Iterables
                .getFirst(Sets.difference(driver.getWindowHandles(), ImmutableSet.of(firstWindow)), null);
        driver.switchTo().window(newWindow);
        HackpadPage hackpadPage = PageFactory.initElements(driver, HackpadPage.class);
        assertEquals(DEFINITION_OF_READY_CONTENTS, hackpadPage.getContent());
        String newHackpadUrl = driver.getCurrentUrl();
        String newHackpadId = StringUtils.substringAfterLast(newHackpadUrl, "/");
        assertNotEquals(DEFINITION_OF_READY_TEMPLATE_ID, newHackpadId);
    }

    @Test
    public void testDashboardReopenBrowser() throws Exception {
        LandingPage landingPage = PageFactory.initElements(driver, LandingPage.class);
        landingPage.loginWithGoogle(getEndpoint());
        assertThat(driver.getWindowHandles(), hasSize(1));
        landingPage.createDefinitionOfReady();

        HtmlUnitDriver driverTwo = new HtmlUnitDriver(BrowserVersion.CHROME);
        driverTwo.setJavascriptEnabled(true);

        // Open a completely new browser with no cookies
        // Verify that view button is visible for this same user,
        // and we're able to open his Definition of ready hackpad
        LandingPage landingPageTwo = PageFactory.initElements(driverTwo, LandingPage.class);
        landingPageTwo.loginWithGoogle(getEndpoint());
        landingPageTwo.waitForDefinitionOfReady();
        assertTrue(landingPageTwo.isDefinitionOfReadyViewable());
        landingPageTwo.viewDefinitionOfReady();
        String newWindow = Iterables.getFirst(
                Sets.difference(driverTwo.getWindowHandles(), ImmutableSet.of(driverTwo.getWindowHandle())), null);
        driverTwo.switchTo().window(newWindow);
        HackpadPage hackpadPage = PageFactory.initElements(driverTwo, HackpadPage.class);
        assertEquals(DEFINITION_OF_READY_CONTENTS, hackpadPage.getContent());
    }

    /**
     * Verifies that different users get different dashboards with links to different documents
     * @throws Exception
     */
    @Test
    public void testSeparateDashboards() throws Exception {
        String templateOne = DEFINITION_OF_READY_CONTENTS + "1";
        String templateTwo = DEFINITION_OF_READY_CONTENTS + "2";

        Person userOne = createUser();
        Person userTwo = createUser();

        // Create hackpad for user #1
        fakeGoogleServer.setPerson(userOne);

        LandingPage landingPage = PageFactory.initElements(driver, LandingPage.class);
        landingPage.loginWithGoogle(getEndpoint());
        fakeHackpadServer.addHackpad(new PadIdentity(DEFINITION_OF_READY_TEMPLATE_ID), templateOne);
        String currentWindow = driver.getWindowHandle();
        landingPage.createDefinitionOfReady();
        closeWindowsExceptFor(driver, currentWindow);

        // Log in with user #2, there should be no definition of ready on the dashboard
        fakeGoogleServer.setPerson(userTwo);

        HtmlUnitDriver driverTwo = new HtmlUnitDriver(BrowserVersion.CHROME);
        driverTwo.setJavascriptEnabled(true);
        LandingPage landingPageTwo = PageFactory.initElements(driverTwo, LandingPage.class);
        landingPageTwo.loginWithGoogle(getEndpoint());
        assertFalse(landingPageTwo.isDefinitionOfReadyViewable());

        // Create another definition of ready
        fakeHackpadServer.addHackpad(new PadIdentity(DEFINITION_OF_READY_TEMPLATE_ID), templateTwo);
        String currentWindowTwo = driverTwo.getWindowHandle();
        landingPageTwo.createDefinitionOfReady();
        closeWindowsExceptFor(driverTwo, currentWindowTwo);

        assertDefinitionOfReadyContents(driver, templateOne);

        assertDefinitionOfReadyContents(driverTwo, templateTwo);
    }

    private static void closeWindowsExceptFor(HtmlUnitDriver driver, String currentWindow) {
        Set<String> difference = ImmutableSet
                .copyOf(Sets.difference(driver.getWindowHandles(), ImmutableSet.of(currentWindow)));
        for (String window : difference) {
            driver.switchTo().window(window).close();
        }
        driver.switchTo().window(currentWindow);
    }

    private static void assertDefinitionOfReadyContents(HtmlUnitDriver driver, String contents) {
        // Refresh the dashboard for user #1; he should have definition of ready viewable
        LandingPage landingPage = PageFactory.initElements(driver, LandingPage.class);
        driver.navigate().refresh();
        String currentWindow = driver.getWindowHandle();
        landingPage.waitForDefinitionOfReady();
        assertTrue(landingPage.isDefinitionOfReadyViewable());
        landingPage.viewDefinitionOfReady();

        // Verify definition of ready contents for user #1
        String newWindow = Iterables
                .getFirst(Sets.difference(driver.getWindowHandles(), ImmutableSet.of(currentWindow)), null);
        driver.switchTo().window(newWindow);
        HackpadPage hackpadPage = PageFactory.initElements(driver, HackpadPage.class);
        assertEquals(contents, hackpadPage.getContent());
    }

    private String getEndpoint() {
        return "http://localhost:" + port + "/";
    }
}