org.fcrepo.integration.FedoraHtmlResponsesIT.java Source code

Java tutorial

Introduction

Here is the source code for org.fcrepo.integration.FedoraHtmlResponsesIT.java

Source

/*
 * Licensed to DuraSpace under one or more contributor license agreements.
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 *
 * DuraSpace licenses this file to you 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.fcrepo.integration;

import static com.gargoylesoftware.htmlunit.BrowserVersion.FIREFOX_45;
import static com.google.common.collect.Lists.transform;
import static java.util.UUID.randomUUID;
import static javax.ws.rs.core.HttpHeaders.ACCEPT;
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_CONFIG_NAMESPACE;
import static org.fcrepo.kernel.api.RdfLexicon.REPOSITORY_NAMESPACE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.entity.BasicHttpEntity;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.IncorrectnessListener;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.SilentCssErrorHandler;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomAttr;
import com.gargoylesoftware.htmlunit.html.DomText;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlFileInput;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSelect;
import com.gargoylesoftware.htmlunit.html.HtmlTextArea;

/**
 * <p>FedoraHtmlResponsesIT class.</p>
 *
 * @author cbeer
 */
public class FedoraHtmlResponsesIT extends AbstractResourceIT {

    private WebClient webClient;
    private WebClient javascriptlessWebClient;

    @Before
    public void setUp() {
        webClient = getDefaultWebClient();

        javascriptlessWebClient = getDefaultWebClient();
        javascriptlessWebClient.getOptions().setJavaScriptEnabled(false);
    }

    @After
    public void cleanUp() {
        webClient.close();
        javascriptlessWebClient.close();
    }

    @Test
    public void testDescribeHtml() throws IOException {
        final HtmlPage page = webClient.getPage(serverAddress);
        ((HtmlElement) page.getFirstByXPath("//h4[text()='Properties']")).click();

        checkForHeaderBranding(page);
        final String namespaceLabel = page.getFirstByXPath("//span[@title='" + REPOSITORY_NAMESPACE + "']/text()")
                .toString();

        assertEquals("Expected to find namespace URIs displayed as their prefixes", "fedora:", namespaceLabel);
    }

    @Test
    public void testCreateNewNodeWithProvidedId() throws IOException {
        createAndVerifyObjectWithIdFromRootPage(randomUUID().toString());
    }

    private HtmlPage createAndVerifyObjectWithIdFromRootPage(final String pid) throws IOException {
        final HtmlPage page = webClient.getPage(serverAddress);
        final HtmlForm form = (HtmlForm) page.getElementById("action_create");
        final HtmlSelect type = (HtmlSelect) page.getElementById("new_mixin");
        type.getOptionByValue("container").setSelected(true);

        final HtmlInput new_id = (HtmlInput) page.getElementById("new_id");
        new_id.setValueAttribute(pid);
        final HtmlButton button = form.getFirstByXPath("button");
        button.click();

        try {
            final HtmlPage page1 = webClient.getPage(serverAddress + pid);
            assertEquals("Page had wrong title!", serverAddress + pid, page1.getTitleText());
            return page1;
        } catch (final FailingHttpStatusCodeException e) {
            fail("Did not successfully retrieve created page! Got HTTP code: " + e.getStatusCode());
            return null;
        }
    }

    @Test
    public void testCreateNewNodeWithGeneratedId() throws IOException {

        final HtmlPage page = webClient.getPage(serverAddress);
        final HtmlForm form = (HtmlForm) page.getElementById("action_create");
        final HtmlSelect type = (HtmlSelect) page.getElementById("new_mixin");
        type.getOptionByValue("container").setSelected(true);
        final HtmlButton button = form.getFirstByXPath("button");
        button.click();

        final HtmlPage page1 = javascriptlessWebClient.getPage(serverAddress);
        assertTrue("Didn't see new information in page!", !page1.asText().equals(page.asText()));
    }

    @Test
    @Ignore("The htmlunit web client can't handle the HTML5 file API")
    public void testCreateNewDatastream() throws IOException {

        final String pid = randomUUID().toString();

        // can't do this with javascript, because HTMLUnit doesn't speak the HTML5 file api
        final HtmlPage page = webClient.getPage(serverAddress);
        final HtmlForm form = (HtmlForm) page.getElementById("action_create");

        final HtmlInput slug = form.getInputByName("slug");
        slug.setValueAttribute(pid);

        final HtmlSelect type = (HtmlSelect) page.getElementById("new_mixin");
        type.getOptionByValue("binary").setSelected(true);

        final HtmlFileInput fileInput = (HtmlFileInput) page.getElementById("datastream_payload");
        fileInput.setData("abcdef".getBytes());
        fileInput.setContentType("application/pdf");

        final HtmlButton button = form.getFirstByXPath("button");
        button.click();

        final HtmlPage page1 = javascriptlessWebClient.getPage(serverAddress + pid);
        assertEquals(serverAddress + pid, page1.getTitleText());
    }

    @Test
    public void testCreateNewDatastreamWithNoFileAttached() throws IOException {

        final String pid = randomUUID().toString();

        // can't do this with javascript, because HTMLUnit doesn't speak the HTML5 file api
        final HtmlPage page = webClient.getPage(serverAddress);
        final HtmlForm form = (HtmlForm) page.getElementById("action_create");

        final HtmlInput slug = form.getInputByName("slug");
        slug.setValueAttribute(pid);

        final HtmlSelect type = (HtmlSelect) page.getElementById("new_mixin");
        type.getOptionByValue("binary").setSelected(true);

        final HtmlButton button = form.getFirstByXPath("button");
        button.click();

        javascriptlessWebClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
        final int status = javascriptlessWebClient.getPage(serverAddress + pid).getWebResponse().getStatusCode();
        assertEquals(NOT_FOUND.getStatusCode(), status);
    }

    @Test
    public void testCreateNewObjectAndDeleteIt() throws IOException {
        final boolean throwExceptionOnFailingStatusCode = webClient.getOptions()
                .isThrowExceptionOnFailingStatusCode();
        webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);

        final String pid = createNewObject();
        final HtmlPage page = webClient.getPage(serverAddress + pid);
        final HtmlForm action_delete = page.getFormByName("action_delete");
        action_delete.getButtonByName("delete-button").click();
        webClient.waitForBackgroundJavaScript(1000);
        webClient.waitForBackgroundJavaScriptStartingBefore(10000);

        final Page page2 = webClient.getPage(serverAddress + pid);
        assertEquals("Didn't get a 410!", 410, page2.getWebResponse().getStatusCode());

        webClient.getOptions().setThrowExceptionOnFailingStatusCode(throwExceptionOnFailingStatusCode);
    }

    /**
     * This test walks through the steps for creating an object, setting some
     * metadata, creating a version, updating that metadata, viewing the
     * version history to find that old version.
     *
     * @throws IOException exception thrown during this function
     */
    @Ignore
    @Test
    public void testVersionCreationAndNavigation() throws IOException {
        final String pid = randomUUID().toString();
        createAndVerifyObjectWithIdFromRootPage(pid);

        final String updateSparql = "PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" + "PREFIX fedora: <"
                + REPOSITORY_NAMESPACE + ">\n" + "\n"
                + "INSERT DATA { <> fedoraconfig:versioningPolicy \"auto-version\" ; dc:title \"Object Title\". }";
        postSparqlUpdateUsingHttpClient(updateSparql, pid);

        final HtmlPage objectPage = javascriptlessWebClient.getPage(serverAddress + pid);
        assertEquals("Auto versioning should be set.", "auto-version",
                objectPage
                        .getFirstByXPath(
                                "//span[@property='" + FEDORA_CONFIG_NAMESPACE + "versioningPolicy']/text()")
                        .toString());
        assertEquals("Title should be set.", "Object Title", objectPage
                .getFirstByXPath("//span[@property='http://purl.org/dc/elements/1.1/title']/text()").toString());

        final String updateSparql2 = "PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" + "\n"
                + "DELETE { <> dc:title ?t }\n" + "INSERT { <> dc:title \"Updated Title\" }"
                + "WHERE { <> dc:title ?t }";
        postSparqlUpdateUsingHttpClient(updateSparql2, pid);

        final HtmlPage versions = javascriptlessWebClient.getPage(serverAddress + pid + "/fcr:versions");
        final List<DomAttr> versionLinks = castList(versions.getByXPath("//a[@class='version_link']/@href"));
        assertEquals("There should be two revisions.", 2, versionLinks.size());

        // get the labels
        // will look like "Version from 2013-00-0T00:00:00.000Z"
        // and will sort chronologically based on a String comparison
        final List<DomText> labels = castList(versions.getByXPath("//a[@class='version_link']/text()"));
        final boolean chronological = labels.get(0).asText().compareTo(labels.get(1).toString()) < 0;
        logger.debug("Versions {} in chronological order: {}, {}", chronological ? "are" : "are not",
                labels.get(0).asText(), labels.get(1).asText());

        final HtmlPage firstRevision = javascriptlessWebClient
                .getPage(versionLinks.get(chronological ? 0 : 1).getNodeValue());
        final List<DomText> v1Titles = castList(
                firstRevision.getByXPath("//span[@property='http://purl.org/dc/elements/1.1/title']/text()"));
        final HtmlPage secondRevision = javascriptlessWebClient
                .getPage(versionLinks.get(chronological ? 1 : 0).getNodeValue());
        final List<DomText> v2Titles = castList(
                secondRevision.getByXPath("//span[@property='http://purl.org/dc/elements/1.1/title']/text()"));

        assertEquals("Version one should have one title.", 1, v1Titles.size());
        assertEquals("Version two should have one title.", 1, v2Titles.size());
        assertNotEquals("Each version should have a different title.", v1Titles.get(0), v2Titles.get(0));
        assertEquals("First version should be preserved.", "Object Title", v1Titles.get(0).getWholeText());
        assertEquals("Second version should be preserved.", "Updated Title", v2Titles.get(0).getWholeText());
    }

    private static void postSparqlUpdateUsingHttpClient(final String sparql, final String pid) throws IOException {
        final HttpPatch method = new HttpPatch(serverAddress + pid);
        method.addHeader(CONTENT_TYPE, "application/sparql-update");
        final BasicHttpEntity entity = new BasicHttpEntity();
        entity.setContent(new ByteArrayInputStream(sparql.getBytes()));
        method.setEntity(entity);
        final HttpResponse response = client.execute(method);
        assertEquals("Expected successful response.", 204, response.getStatusLine().getStatusCode());
    }

    @Test
    @Ignore
    public void testCreateNewObjectAndSetProperties() throws IOException {
        final String pid = createNewObject();

        final HtmlPage page = javascriptlessWebClient.getPage(serverAddress + pid);
        final HtmlForm form = (HtmlForm) page.getElementById("action_sparql_update");
        final HtmlTextArea sparql_update_query = (HtmlTextArea) page.getElementById("sparql_update_query");
        sparql_update_query.setText("INSERT { <> <info:some-predicate> 'asdf' } WHERE { }");

        final HtmlButton button = form.getFirstByXPath("button");
        button.click();

        final HtmlPage page1 = javascriptlessWebClient.getPage(serverAddress + pid);
        assertTrue(page1.getElementById("metadata").asText().contains("some-predicate"));
    }

    @Test
    @Ignore("htmlunit can't see links in the HTML5 <nav> element..")
    public void testSparqlSearch() throws IOException {
        final HtmlPage page = webClient.getPage(serverAddress);

        logger.error(page.toString());
        page.getAnchorByText("Search").click();

        final HtmlForm form = (HtmlForm) page.getElementById("action_sparql_select");
        final HtmlTextArea q = form.getTextAreaByName("q");
        q.setText("SELECT ?subject WHERE { ?subject a <" + REPOSITORY_NAMESPACE + "Resource> }");
        final HtmlButton button = form.getFirstByXPath("button");
        button.click();
    }

    private static void checkForHeaderBranding(final HtmlPage page) {
        assertNotNull(page
                .getFirstByXPath("//nav[@role='navigation']/div[@class='navbar-header']/a[@class='navbar-brand']"));
    }

    private String createNewObject() throws IOException {

        final String pid = randomUUID().toString();

        final HtmlPage page = webClient.getPage(serverAddress);
        final HtmlForm form = page.getFormByName("action_create");

        final HtmlInput slug = form.getInputByName("slug");
        slug.setValueAttribute(pid);

        final HtmlButton button = form.getFirstByXPath("button");
        button.click();

        webClient.waitForBackgroundJavaScript(1000);
        webClient.waitForBackgroundJavaScriptStartingBefore(10000);

        return pid;
    }

    private CredentialsProvider getFedoraAdminCredentials() {
        final CredentialsProvider credentials = new DefaultCredentialsProvider();
        credentials.setCredentials(AuthScope.ANY, FEDORA_ADMIN_CREDENTIALS);
        return credentials;
    }

    private WebClient getDefaultWebClient() {

        final WebClient webClient = new WebClient(FIREFOX_45);
        webClient.addRequestHeader(ACCEPT, "text/html");
        webClient.setCredentialsProvider(getFedoraAdminCredentials());
        webClient.waitForBackgroundJavaScript(1000);
        webClient.waitForBackgroundJavaScriptStartingBefore(10000);
        webClient.setAjaxController(new NicelyResynchronizingAjaxController());
        //Suppress warning from IncorrectnessListener
        webClient.setIncorrectnessListener(new SuppressWarningIncorrectnessListener());

        //Suppress css warning with the silent error handler.
        webClient.setCssErrorHandler(new SilentCssErrorHandler());
        return webClient;

    }

    @SuppressWarnings("unchecked")
    private static <T> List<T> castList(final List<?> l) {
        return transform(l, x -> (T) x);
    }

    private static class SuppressWarningIncorrectnessListener implements IncorrectnessListener {
        @Override
        public void notify(final String arg0, final Object arg1) {

        }
    }

}