/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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 test.web;

import static java.lang.String.format;
import static org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext;

import java.util.Locale;

import javax.servlet.ServletContext;

import net.sourceforge.jwebunit.junit.WebTester;
import net.sourceforge.jwebunit.util.TestingEngineRegistry;

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.webapp.WebAppContext;
import org.springframework.aop.support.AopUtils;
import org.springframework.config.java.context.JavaConfigWebApplicationContext;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.OwnerTests;


/**
 * Tests that run within an embedded Jetty servlet container in order to
 * prove that web.xml is well-formed, data sources are wired up appropriately,
 * etc.  Runs through all the major use cases in the webapp to ensure that links
 * are properly formed, forms have accurate action values and parameters.
 *
 * @author Chris Beams
 */
@Ignore // temporary while 503 errors are diagnosed
public class InContainerTests {

    private static final String CONTEXT_ROOT = "src/main/webapp";

    private static Server server;
    private static WebAppContext webAppContext;
    private WebTester tester;

    @BeforeClass
    public static void startServer() throws Exception {
        server = new Server(0);
        webAppContext = new WebAppContext(CONTEXT_ROOT, "/petclinic");
        server.addHandler(webAppContext);
        server.start();
    }

    @AfterClass
    public static void stopServer() throws Exception {
        server.stop();
    }

    @Before
    public void setUp() throws Exception {
        tester = new WebTester();
        tester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT);

        // getLocalPort returns the port that was actually assigned
        final int actualPort = server.getConnectors()[0].getLocalPort();
        final String baseUrl = "http://localhost:" + actualPort + "/";
        tester.getTestContext().setBaseUrl(baseUrl);
    }

    @Test
    public void testIndex() {
        // index should redirect us to welcome.do
        tester.beginAt("/petclinic/");

        // shows that I18N infrastructure is in place
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        String welcomeString = messageSource.getMessage("welcome", null, Locale.getDefault());

        tester.assertTextPresent(welcomeString);

        tester.assertLinkPresentWithText("Find owner");
        tester.clickLinkWithExactText("Find owner");
        tester.assertTextPresent("Find Owners:");
    }
    
    @Test
    public void testListVets() {
        tester.beginAt("/petclinic");
        
        tester.clickLinkWithExactText("Display all veterinarians");
        
        tester.assertTextPresent("James Carter");
        tester.assertTextPresent("Linda Douglas");
    }

    @Test
    public void testFindOwners() {
        tester.beginAt("/petclinic/main/owner/find");

        tester.assertSubmitButtonPresent();

        // submit an empty form -> we should get a list of owners back
        tester.submit();

        // if the database is not up, this is where we'll find out!
        tester.assertTextNotPresent("Data access failure");

        // a list of owners should now be present
        tester.assertTextPresent("George Franklin");
        tester.assertTextPresent("Betty Davis");
    }
    
    @Test
    public void testEditOwner() {
        tester.beginAt("petclinic/main/owner/show?id=1");
        
        tester.assertTextPresent("George Franklin");
        
        tester.submit("", "Edit Owner");
        
        tester.assertTextPresent("Owner:");
        
        tester.setTextField("firstName", "Changed");
        
        tester.submit("", "Update Owner");
        
        tester.assertTextPresent("Changed Franklin");
    }
    
    @Test
    public void testAddPet() {
        tester.beginAt("petclinic/main/owner/show?id=1");
        
        // go to the add pet form
        tester.submit("", "Add New Pet");
        
        // submit the form without filling out any fields: invalid
        tester.submit("", "Add Pet");

        // assert that we see error text as expected
        tester.assertTextPresent("is required");
        tester.assertTextPresent("invalid date");
        
        // now, fill it out and submit.
        tester.setTextField("name", "fido");
        tester.setTextField("birthDate", "2008-01-01");
        tester.selectOption("type", "dog");
        tester.submit("", "Add Pet");
        
        // assert that we're back on the owner show page and that we can see fido
        tester.assertTextPresent("Pets and Visits");
        tester.assertTextPresent("fido");
    }
    
    @Test
    public void testEditPet() {
        tester.beginAt("petclinic/main/owner/show?id=1");
        
        // go to the edit pet form for the pet with id '1' (Leo)
        tester.submit("submitEditPet1", "Edit Pet");
        
        // determine that we're editing the right pet
        tester.assertTextPresent("Leo");
        
        // change Leo's name in the form
        tester.setTextField("name", "LeoChanged");
        
        // submit the edit pet form
        tester.submit();
        
        // assert the changes have shown up
        tester.assertTextPresent("Pets and Visits");
        tester.assertTextPresent("LeoChanged");
    }
    
    @Test
    public void testAddOwner() {
        // begin at the find owners form
        tester.beginAt("petclinic/main/owner/find");
        
        // let's add a new owner
        tester.clickLinkWithExactText("Add Owner");
        
        // submit the new owner form without filling any fields in
        tester.submit();
        
        // should see a bunch of errors telling us to fill the fields in
        tester.assertTextPresent("is required");
        
        // fill out the form with known-valid values (except for phone)
        Owner validOwner = OwnerTests.createValidOwner();
        tester.setTextField("firstName", validOwner.getFirstName());
        tester.setTextField("lastName", validOwner.getLastName());
        tester.setTextField("address", validOwner.getAddress());
        tester.setTextField("city", validOwner.getCity());
        tester.setTextField("telephone", "206-555-1212"); // invalid!
        
        // submit the form with an invalid phone number
        tester.submit();
        tester.assertTextPresent("must be all numeric");
        
        // correct the phone number problem and re-submit
        tester.setTextField("telephone", validOwner.getTelephone());
        tester.submit();
        
        // assert that we end up on the show page with the new info
        tester.assertTextPresent("Owner Information");
        tester.assertTextPresent(validOwner.getFirstName() + " " + validOwner.getLastName());
    }
    
    @Test
    public void testAddVisit() {
        tester.beginAt("petclinic/main/owner/show?id=1");
        
        tester.submit("submitAddVisit1");
        
        tester.assertTextPresent("New Visit");
        
        tester.submit();
        
        tester.assertTextPresent("is required");
        
        tester.setTextField("description", "basic check-up");
        
        tester.submit();
        
        tester.assertTextPresent("Pets and Visits");
        tester.assertTextPresent("basic check-up");
    }

    @Test
    public void testRootAppContextRegistration() throws Exception {
        // will throw if the root app ctx has not been registered
        getApplicationContext();
    }

    @Test
    public void testClinicBeanExistsAndIsTxProxy() {
        JavaConfigWebApplicationContext ctx = getApplicationContext();

        String serviceBeanName = "clinic";
        Assert.assertTrue(format("context did not contain @Bean [%s] as expected", serviceBeanName),
                          ctx.containsBean(serviceBeanName));

        Clinic service = ctx.getBean(Clinic.class, serviceBeanName);
        Assert.assertTrue("service was not transactional as expected", AopUtils.isAopProxy(service));
    }

    private JavaConfigWebApplicationContext getApplicationContext() {
        ServletContext servletContext = webAppContext.getServletContext();
        return (JavaConfigWebApplicationContext) getRequiredWebApplicationContext(servletContext);
    }

}
