// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation // // 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 org.apache.tapestry5.integration; import org.apache.tapestry5.corelib.components.Form; import org.apache.tapestry5.corelib.mixins.RenderDisabled; import org.apache.tapestry5.integration.app1.data.RegistrationData; import org.apache.tapestry5.integration.app1.pages.RenderErrorDemo; import org.apache.tapestry5.internal.TapestryInternalUtils; import org.apache.tapestry5.test.AbstractIntegrationTestSuite; import org.testng.annotations.Test; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.InputStream; import java.net.URL; /** * Note: If these tests fail with BindException when starting Jetty, it could be Skype. At least on my system, Skype is * listening on localhost:80. */ @SuppressWarnings({ "JavaDoc" }) @Test(timeOut = 50000, sequential = true) public class IntegrationTests extends AbstractIntegrationTestSuite { public IntegrationTests() { super("src/test/app1"); } @Test public void assets() throws Exception { start("AssetDemo"); // Test for https://issues.apache.org/jira/browse/TAPESTRY-1935 // assertSourcePresent(""); // Read the byte stream for the asset and compare to the real copy. compareDownloadedAsset(getAttribute("//img[@id='icon']/@src"), "src/test/app1/images/tapestry_banner.gif"); compareDownloadedAsset(getAttribute("//img[@id='button']/@src"), "src/test/resources/org/apache/tapestry5/integration/app1/pages/nested/tapestry-button.png"); compareDownloadedAsset(getAttribute("//img[@id='viaContext']/@src"), "src/test/app1/images/asf_logo_wide.gif"); } private void compareDownloadedAsset(String assetURL, String localPath) throws Exception { URL url = new URL("http", "localhost", JETTY_PORT, assetURL); byte[] downloaded = readContent(url); File local = new File(localPath); byte[] actual = readContent(local.toURL()); assertEquals(downloaded, actual); } /** * Tests the ability to inject a Block, and the ability to use the block to control rendering. */ @Test public void block_rendering() throws Exception { start("BlockDemo"); assertTextPresent("[]"); select("//select[@id='blockName']", "fred"); waitForPageToLoad(PAGE_LOAD_TIMEOUT); assertTextPresent("[Block fred.]"); select("//select[@id='blockName']", "barney"); waitForPageToLoad(PAGE_LOAD_TIMEOUT); assertTextPresent("[Block barney.]"); // TAPESETRY-1583 assertTextPresent("before it is defined: [Block wilma]."); } @Test public void component_parameter_default_from_method() throws Exception { start("ParameterDefault"); assertTextPresent("Echo component default: [ParameterDefault:echo]"); } @Test public void embedded_components() { start("Countdown Page"); assertTextPresent("regexp:\\s+5\\s+4\\s+3\\s+2\\s+1\\s+"); assertTextPresent("Brought to you by the org.apache.tapestry5.integration.app1.components.Count"); } @Test public void encoded_loop_inside_a_form() { test_loop_inside_form("ToDo List"); } @Test public void environmental() { start("Environmental Annotation Usage"); assertSourcePresent("[A message provided by the RenderableProvider component.]"); } @Test public void exception_report() { start("BadTemplate Page"); assertTextPresent("org.apache.tapestry5.ioc.internal.util.TapestryException", "Failure parsing template classpath:org/apache/tapestry5/integration/app1/pages/BadTemplate.tml, line 7, column 15", "content from template", "Element is in the Tapestry namespace, but is not a recognized Tapestry template element."); } @Test public void expansion() { start("Expansion Page"); assertTextPresent("[value provided by a template expansion]"); } /** * {@link org.apache.tapestry5.internal.transform.InjectContainerWorker} is largely tested by the forms tests * ({@link RenderDisabled} is built on it). test is for the failure case, where a mixin class is used with the wrong * type of component. */ @Test public void inject_container_failure() throws Exception { start("InjectContainerMismatch"); // And exception message: assertTextPresent( "Component InjectContainerMismatch is not assignable to field org.apache.tapestry5.corelib.mixins.RenderDisabled.field (of type org.apache.tapestry5.Field)."); } @Test public void inject_component_failure() throws Exception { start("InjectComponentMismatch"); assertTextPresent( "Unable to inject component 'form' into field form of component InjectComponentMismatch. Class org.apache.tapestry5.corelib.components.BeanEditForm is not assignable to a field of type org.apache.tapestry5.corelib.components.Form.", "ClassCastException"); } @Test public void injection() throws Exception { start("Inject Demo"); // is a test for a named @Inject: assertTextPresent(""); // is a test for an anonymous @Inject and ComponentResourcesInjectionProvider assertTextPresent("ComponentResources[InjectDemo]"); // Another test, DefaultInjectionProvider assertTextPresent(""); // Prove that injection using a marker annotation (to match against a marked service) works. assertTextPresent("Injection via Marker: Bonjour!"); assertText("viaInjectService", "1722 tracks in music library"); } @Test public void instance_mixin() { start("InstanceMixin"); final String[] dates = { "Jun 13, 1999", "Jul 15, 2001", "Dec 4, 2005" }; for (String date : dates) { String snippet = String.format("[%s]", date); assertSourcePresent(snippet); } clickAndWait("link=Toggle emphasis"); for (String date : dates) { String snippet = String.format("[%s]", date); assertSourcePresent(snippet); } } @Test public void localization() { start("Localization"); assertTextPresent("Via injected Messages property: [Accessed via injected Messages]"); assertTextPresent("Via message: binding prefix: [Accessed via message: binding prefix]"); assertTextPresent("From Application Message Catalog: [Application Catalog Working]"); assertTextPresent("Page locale: [en]"); clickAndWait("link=French"); assertTextPresent("Page locale: [fr]"); clickAndWait("link=English"); assertTextPresent("Page locale: [en]"); } @Test public void page_injection() throws Exception { start("Inject Demo"); clickAndWait("link=Fred"); assertTextPresent("You clicked Fred."); clickAndWait("link=Back"); clickAndWait("link=Barney"); assertTextPresent("You clicked Barney."); clickAndWait("link=Back"); clickAndWait("link=Wilma"); assertTextPresent("You clicked Wilma."); } @Test public void passivate_activate() throws Exception { start("NumberSelect"); clickAndWait("link=5"); assertTextPresent("You chose 5."); } @Test public void password_field() { start("PasswordFieldDemo"); type("userName", "howard"); type("password", "wrong-password"); clickAndWait(SUBMIT); assertFieldValue("userName", "howard"); // Verify that password fields do not render a non-blank password, even when it is known. assertFieldValue("password", ""); assertTextPresent("[howard]"); assertTextPresent("[wrong-password]"); type("password", "tapestry"); clickAndWait(SUBMIT); assertTextPresent("You have provided the correct user name and password."); } @Test public void render_phase_method_returns_a_component() throws Exception { start("RenderComponentDemo"); assertText("//span[@id='container']", "[]"); // Sneak in a little test for If and parameter else: assertTextPresent("Should be blank:"); clickAndWait("enabled"); // After clicking the link (which submits the form), the page re-renders and shows us // the optional component from inside the NeverRender, resurrected to render on the page // after all. assertText("//span[@id='container']/span", "Optional Text"); assertTextPresent("Should now show up:"); } @Test public void render_phase_order() { start("RenderPhaseOrder"); assertTextPresent( "[BEGIN-TRACER-MIXIN BEGIN-ABSTRACT-TRACER BEGIN-TRACER BODY AFTER-TRACER AFTER-ABSTRACT-TRACER AFTER-TRACER-MIXIN]"); } @Test public void server_side_validation_for_textfield_and_textarea() throws Exception { start("ValidForm"); clickAndWait(SUBMIT); assertTextPresent("You must provide a value for Email."); // is an overridden validation error message: assertTextPresent("Please provide a detailed description of the incident."); // Check on decorations via the default validation decorator: assertAttribute("//label[1]/@class", "t-error"); assertAttribute("//label[2]/@class", "t-error"); assertAttribute("//input[@id='email']/@class", "t-error"); assertAttribute("//textarea[@id='message']/@class", "t-error"); type("email", "foo@bar.baz"); type("message", "Show me the money!"); type("hours", "foo"); clickAndWait(SUBMIT); assertTextPresent("[false]"); assertTextPresent("You must provide an integer value for Hours."); assertAttribute("//input[@id='hours']/@value", "foo"); type("hours", " 19 "); click("//input[@id='urgent']"); clickAndWait(SUBMIT); // Make sure the decoration went away. // Sorry, not sure how to do that, since the attributes don't exist, we get xpath errors. // assertText("//label[1]/@class", ""); // assertText("//label[2]/@class", ""); // assertText("//input[@id='email']/@class", ""); // assertText("//textarea[@id='message']/@class", ""); assertTextPresent("[foo@bar.baz]"); assertTextPresent("[Show me the money!]"); assertTextPresent("[true]"); assertTextPresent("[19]"); } @Test public void simple_component_event() { final String YOU_CHOSE = "You chose: "; start("Action Page"); assertFalse(isTextPresent(YOU_CHOSE)); for (int i = 2; i < 5; i++) { clickAndWait("link=" + i); assertTextPresent(YOU_CHOSE + i); } } /** * Tests for forms and form submissions and basic form control components. also tests a few other things, such as * computed default bindings and invisible instrumentation. */ @Test public void simple_form() { start("SimpleForm"); assertText("//label[@id='disabled-label']", "Disabled"); // This demonstrates TAPESTRY-1642: assertText("//label[@id='email-label']", "User Email"); assertText("//label[@id='message-label']", "Incident Message"); assertText("//label[@id='operatingSystem-label']", "Operating System"); assertText("//label[@id='department-label']", "Department"); assertText("//label[@id='urgent-label']", "Urgent Processing Requested"); assertFieldValue("email", ""); assertFieldValue("message", ""); assertFieldValue("operatingSystem", "osx"); assertFieldValue("department", ""); assertFieldValue("urgent", "on"); clickAndWait(SUBMIT); assertTextPresent("department: []"); type("email", "foo@bar.baz"); type("message", "Message for you, sir!"); select("operatingSystem", "Windows NT"); select("department", "R&D"); click("urgent"); clickAndWait(SUBMIT); assertFieldValue("email", "foo@bar.baz"); assertFieldValue("message", "Message for you, sir!"); assertFieldValue("urgent", "off"); // Tried to use "email:" and "exact:email:" but Selenium 0.8.1 doesn't seem to accept that. assertTextPresent("[foo@bar.baz]", "[Message for you, sir!]", "[false]", "[winnt]", "[RESEARCH_AND_DESIGN]"); // Haven't figured out how to get selenium to check that fields are disabled. } @Test public void subclass_inherits_parent_template() { start("ExpansionSubclass"); assertTextPresent("[value provided, in the subclass, via a template expansion]"); } @Test public void template_overridden() { start("Template Overridden by Class Page"); assertTextPresent("Output: ClassValue"); } @Test public void volatile_loop_inside_a_form() { test_loop_inside_form("ToDo List (Volatile)"); } /** * also verifies the use of meta data to set the default strategy. */ @Test public void flash_persistence() { start("FlashDemo"); assertTextPresent("[]"); clickAndWait("link=show the message"); assertTextPresent("[You clicked the link!]"); clickAndWait("link=refresh the page"); assertTextPresent("[]"); } private byte[] readContent(URL url) throws Exception { InputStream is = new BufferedInputStream(url.openStream()); ByteArrayOutputStream os = new ByteArrayOutputStream(); TapestryInternalUtils.copy(is, os); os.close(); is.close(); return os.toByteArray(); } private void test_loop_inside_form(String linkLabel) { start(linkLabel); clickAndWait("link=reset the database"); assertFieldValue("title", "End World Hunger"); assertFieldValue("title_0", "Develop Faster-Than-Light Travel"); assertFieldValue("title_1", "Cure Common Cold"); type("title", "End World Hunger - today"); type("title_0", "Develop Faster-Than-Light Travel - immediately"); type("title_1", "Cure Common Cold - post haste"); clickAndWait("//input[@value='Update ToDos']"); assertFieldValue("title", "End World Hunger - today"); assertFieldValue("title_0", "Develop Faster-Than-Light Travel - immediately"); assertFieldValue("title_1", "Cure Common Cold - post haste"); clickAndWait("//input[@value='Add new ToDo']"); type("title_2", "Conquer World"); clickAndWait("//input[@value='Update ToDos']"); assertFieldValue("title", "End World Hunger - today"); assertFieldValue("title_0", "Develop Faster-Than-Light Travel - immediately"); assertFieldValue("title_1", "Cure Common Cold - post haste"); assertFieldValue("title_2", "Conquer World"); } /** * Tests the bean editor. Along the way, tests a bunch about validation, loops, blocks, and application state * objects. */ @Test public void bean_editor() { start("BeanEditor Demo", "Clear Data"); clickAndWait(SUBMIT); // Part of the override for the firstName property assertAttribute("//input[@id='firstName']/@size", "40"); // Check that the @Width annotation works assertAttribute("//input[@id='birthYear']/@size", "4"); // Check override of the submit label assertAttribute("//input[@type='submit']/@value", "Register"); type("firstName", "a"); type("lastName", "b"); type("birthYear", ""); select("sex", "label=Martian"); click("citizen"); type("password", "abracadabra"); type("notes", "line1\nline2\nline3"); clickAndWait(SUBMIT); assertTextPresent("You must provide at least 3 characters for First Name.", "You must provide at least 5 characters for Last Name.", "You must provide a value for Year of Birth."); type("firstName", "Howard"); type("lastName", "Lewis Ship"); type("birthYear", "1966"); type("password", "supersecret"); clickAndWait(SUBMIT); // The XPath support is too weak for //div[@class='t-beandisplay-value'][%d], so we // just look for the text itself. assertTextPresent("Howard", "Lewis Ship", "1966", "Martian", "U. S. Citizen", "***********", "line1", "line2", "line3"); } @Test public void bean_editor_property_reorder_remove() { start("BeanEdit Remove/Reorder", "Clear Data"); // Looks like a bug in Selenium; we can see //label[1] but not //label[2]. // assertTextSeries("//label[%d]", 1, "Last Name", "First Name", "Sex", "U.S. Citizen"); type("firstName", "Howard"); type("lastName", "Lewis Ship"); type("password", "supersecret"); clickAndWait("//input[@type=\'submit\']"); assertTextPresent("Howard", "Lewis Ship", "0", "100% He-Man", "U. S. Citizen"); } @Test public void pageloaded_lifecycle_method_invoked() { start("PageLoaded Demo"); assertTextPresent("[pageLoaded() was invoked.]"); } /** * Basic Grid rendering, with a column render override. Also tests sorting. */ @Test public void basic_grid() { start("Grid Demo"); // "Sort Rating" via the header cell override (TAPESTRY-2081) assertTextSeries("//th[%d]", 1, "Title", "Album", "Artist", "Genre", "Play Count", "Sort Rating"); // Strange: I thought tr[1] was the header row ??? assertTextSeries("//tr[1]/td[%d]", 1, "Bug Juice", "Late Lounge (2 of 2)", "45 Dip", "Electronica", "4", "-"); // Here were checking that the page splits are correct clickAndWait("link=3"); // Last on page 3: assertText("//tr[25]/td[1]", "Blood Red River"); clickAndWait("link=4"); assertText("//tr[1]/td[1]", "Devil Song"); clickAndWait("link=7"); clickAndWait("link=10"); // Here's one with a customized rating cell assertTextSeries("//tr[25]/td[%d]", 1, "Smoked", "London (Original Motion Picture Soundtrack)", "The Crystal Method", "Soundtrack", "30", "****"); clickAndWait("link=69"); assertText("//tr[22]/td[1]", "radioioAmbient"); // Sort ascending (and we're on the last page, with the highest ratings). clickAndWait("link=Sort Rating"); assertTextSeries("//tr[22]/td[%d]", 1, "Mona Lisa Overdrive", "Labyrinth", "Juno Reactor", "Dance", "31", "*****"); // Toggle to sort descending clickAndWait("link=Sort Rating"); assertTextSeries("//tr[1]/td[%d]", 1, "Hey Blondie", "Out from Out Where"); clickAndWait("link=Title"); // The lack of a leading slash indicates that the path was optimized, see TAPESTRY-1502 assertAttribute("//img[@class='t-sort-icon']/@src", "/assets/tapestry/UNKNOWN/corelib/components/sort-asc.png"); assertAttribute("//img[@class='t-sort-icon']/@alt", "[Asc]"); clickAndWait("link=1"); assertText("//tr[1]/td[1]", "(untitled hidden track)"); clickAndWait("link=Title"); assertAttribute("//img[@class='t-sort-icon']/@src", "/assets/tapestry/UNKNOWN/corelib/components/sort-desc.png"); assertAttribute("//img[@class='t-sort-icon']/@alt", "[Desc]"); clickAndWait("link=reset the Grid"); // Back to where we started. assertTextSeries("//tr[1]/td[%d]", 1, "Bug Juice", "Late Lounge (2 of 2)", "45 Dip", "Electronica", "4", "-"); } @Test public void grid_remove_reorder() { start("Grid Remove/Reorder Demo"); assertTextSeries("//th[%d]", 1, "Rating", "Title", "Album", "Artist", "Genre"); } @Test public void grid_from_explicit_interface_model() { start("SimpleTrack Grid Demo"); assertTextSeries("//th[%d]", 1, "Title", "Album", "Rating"); assertTextSeries("//tr[1]/td[%d]", 1, "Bug Juice", "Late Lounge (2 of 2)", "-"); } @Test public void grid_enum_display() { start("Grid Enum Demo", "reset"); assertTextSeries("//tr[1]/td[%d]", 1, "End World Hunger", "Medium"); assertTextSeries("//tr[2]/td[%d]", 1, "Develop Faster-Than-Light Travel", "Ultra Important"); assertTextSeries("//tr[3]/td[%d]", 1, "Cure Common Cold", "Low"); } @Test public void null_grid() throws Exception { start("Null Grid"); assertTextPresent("There is no data to display."); } @Test public void grid_set() throws Exception { start("Grid Set Demo"); assertFalse(isTextPresent("Exception")); // Also check for TAPESTRY-2228 assertAttribute("//table/@informal", "supported"); } @Test public void navigation_response_from_page_activate() throws Exception { start("Protected Page"); assertText("pagetitle", "Security Alert"); // The message is set by Protected, but is rendered by SecurityAlert. assertTextPresent("Access to Protected page is denied"); } @Test public void mixed_page_activation_context_and_component_context() { start("Kicker"); clickAndWait("link=kick target"); assertTextSeries("//li[%d]", 1, "betty", "wilma", "betty/wilma", "\u82B1\u5B50"); assertTextPresent("No component context."); clickAndWait("link=go"); assertTextSeries("//li[%d]", 1, "betty", "wilma", "betty/wilma", "\u82B1\u5B50"); assertTextSeries("//ul[2]/li[%d]", 1, "fred", "barney", "clark kent", "fred/barney", "\u592A\u90CE"); } @Test public void page_link_with_explicit_empty_context() { start("Kicker"); clickAndWait("link=kick target"); assertTextSeries("//li[%d]", 1, "betty", "wilma", "betty/wilma", "\u82B1\u5B50"); clickAndWait("link=Target base, no context"); assertTextPresent("No activation context."); } @Test public void page_link_with_explicit_activation_context() { start("PageLink Context Demo", "no context"); assertTextPresent("No activation context."); clickAndWait("link=PageLink Context Demo"); clickAndWait("link=literal context"); assertText("//li[1]", "literal context"); clickAndWait("link=PageLink Context Demo"); clickAndWait("link=computed context"); assertTextSeries("//li[%d]", 1, "fred", "7", "true"); clickAndWait("link=PageLink Context Demo"); clickAndWait("link=unsafe characters"); assertText("//li[1]", "unsafe characters: !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"); clickAndWait("link=PageLink Context Demo"); clickAndWait("link=japanese kanji"); assertText("//li[1]", "japanese kanji: \u65E5\u672C\u8A9E"); // TAPESTRY-2221 clickAndWait("link=PageLink Context Demo"); clickAndWait("link=Null in context"); assertText("//li[1]", "NULL"); } @Test public void page_context_in_form() { start("Page Context in Form"); assertTextSeries("//li[%d]", 1, "betty", "wilma", "context with spaces", "context/with/slashes"); assertFieldValue("t:ac", "betty/wilma/context$0020with$0020spaces/context$002fwith$002fslashes"); clickAndWait(SUBMIT); assertTextSeries("//li[%d]", 1, "betty", "wilma", "context with spaces", "context/with/slashes"); assertFieldValue("t:ac", "betty/wilma/context$0020with$0020spaces/context$002fwith$002fslashes"); } @Test public void client_side_validation() { start("Client Validation Demo"); // Used to ensure that the