You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/12/16 01:56:58 UTC
svn commit: r604534 - in /tapestry/tapestry5/trunk/tapestry-core/src:
main/java/org/apache/tapestry/
main/java/org/apache/tapestry/internal/services/
main/java/org/apache/tapestry/internal/structure/
test/java/org/apache/tapestry/internal/services/
Author: hlship
Date: Sat Dec 15 16:56:56 2007
New Revision: 604534
URL: http://svn.apache.org/viewvc?rev=604534&view=rev
Log:
TAPESTRY-1949: Component action requests where the action context contains a period are not parsed correctly leading to request failures
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/Page.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java Sat Dec 15 16:56:56 2007
@@ -39,20 +39,24 @@
/**
* Return a string consisting the concatinated ids of all containing components, separated by
* periods. In addition, nested ids are always all lower case. I.e., "foo.bar.baz". Returns null
- * for a page.
+ * for the root component of a page.
*/
String getNestedId();
/**
- * Creates a component action request link as a callback for this component.
+ * Creates a component action request link as a callback for this component. The event type
+ * and context (as well as the page name and nested component id) will be encoded into a URL. A request for the
+ * URL will {@linkplain #triggerEvent(String, Object[], ComponentEventHandler)} trigger} the named event
+ * on the component.
*
- * @param action a name for the action associated with the link
- * @param forForm if true, the link will be used as the action for an HTML form submission, which
- * may affect what information is encoded into the link
- * @param context additional objects to be encoded into the path portion of the link; each is
- * converted to a string an URI encoded
+ * @param eventType the type of event to be triggered. Event types should be Java identifiers (contain only letters, numbers and the underscore).
+ * @param forForm if true, the link will be used as the eventType for an HTML form submission, which
+ * may affect what information is encoded into the link
+ * @param context additional objects to be encoded into the path portion of the link; each is
+ * converted to a string and URI encoded
+ * @return link object for the callback
*/
- Link createActionLink(String action, boolean forForm, Object... context);
+ Link createActionLink(String eventType, boolean forForm, Object... context);
/**
* Creates a render request link to a specific page.
@@ -66,9 +70,9 @@
Link createPageLink(String pageName, boolean override, Object... context);
/**
- * Returns a string consisting of the fully qualified class name of the containing page, and the
+ * Returns a string consisting of the logical name of the containing page, and the
* {@link #getNestedId() nested id} of this component, separated by a colon. I.e.,
- * "MyPage:foo.bar.baz". For a page, returns just the page's logical name.
+ * "mypage:foo.bar.baz". For a page, returns just the page's logical name.
* <p/>
* This value is often used to obtain an equivalent component instance in a later request.
*
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ActionLinkTarget.java Sat Dec 15 16:56:56 2007
@@ -28,9 +28,9 @@
private final String _componentNestedId;
- public ActionLinkTarget(String action, String pageName, String componentNestedId)
+ public ActionLinkTarget(String eventType, String pageName, String componentNestedId)
{
- _eventType = action;
+ _eventType = eventType;
_pageName = pageName;
_componentNestedId = componentNestedId;
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentActionDispatcher.java Sat Dec 15 16:56:56 2007
@@ -20,10 +20,32 @@
import org.apache.tapestry.services.*;
import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Processes component action events sent as requests from the client. Action events include an
* event type, identify a page and a component, and may provide additional context strings.
+ * <p/>
+ * <p/>
+ * Forms:
+ * <ul>
+ * <li>/context/pagename:eventname -- event on the page, no action context</li>
+ * <li>/context/pagename:eventname/foo/bar -- event on the page with action context "foo", "bar"</li>
+ * <li>/context/pagename.foo.bar -- event on component foo.bar within the page, default event, no action context</li>
+ * <li>/context/pagename.foo.bar/baz.gnu -- event on component foo.bar within the page, default event, with action context "baz", "gnu"</li>
+ * <li>/context/pagename.bar.baz:eventname/foo/gnu -- event on component bar.baz within the page with action context "foo"
+ * , "gnu"</li>
+ * </ul>
+ * <p/>
+ * <p/>
+ * The page name portion may itself consist of a series of folder names, i.e., "admin/user/create". The context portion isn't the concern
+ * of this code, since {@link org.apache.tapestry.services.Request#getPath()} will already have stripped that off. We can act as if the context is
+ * always "/" (the path always starts with a slash).
+ * <p/>
+ * <p/>
+ *
+ * @see org.apache.tapestry.internal.services.LinkFactory#createActionLink(org.apache.tapestry.internal.structure.ComponentPageElement, String, boolean, Object[])
*/
public class ComponentActionDispatcher implements Dispatcher
{
@@ -40,81 +62,66 @@
_componentClassResolver = componentClassResolver;
}
+ // A beast that recognizes all the elements of a path in a single go.
+ // We skip the leading slash, then take the next few terms (until a dot or a colon)
+ // as the page name. Then there's a sequence that sees a dot
+ // and recognizes the nested component id (which may be missing), which ends
+ // at the colon, or at the slash (or the end of the string). The colon identifies
+ // the event name (the event name is also optional). A valid path will always have
+ // a nested component id or an event name (or both) ... when both are missing, then the
+ // path is most likely a page render request. After the optional event name,
+ // the next piece is the action context, which is the remainder of the path.
+
+ private final Pattern PATH_PATTERN = Pattern.compile(
+
+ "^/" + // The leading slash is recognized but skipped
+ "(((\\w+)/)*(\\w+))" + // A series of folder names leading up to the page name, forming the logical page name
+ "(\\.(\\w+(\\.\\w+)*))?" + // The first dot separates the page name from the nested component id
+ "(\\:(\\w+))?" + // A colon, then the event type
+ "(/(.*))?", // A slash, then the action context
+ Pattern.COMMENTS);
+
+ // Constants for the match groups in the above pattern.
+ private static final int LOGICAL_PAGE_NAME = 1;
+ private static final int NESTED_ID = 6;
+ private static final int EVENT_NAME = 9;
+ private static final int CONTEXT = 11;
+
+ // Used to split a context into individual chunks.
+
+ private final Pattern SLASH_PATTERN = Pattern.compile("/");
+
public boolean dispatch(Request request, Response response) throws IOException
{
String path = request.getPath();
- String logicalPageName = null;
- String nestedComponentId = "";
- String eventType = TapestryConstants.ACTION_EVENT;
-
- // Will always have a dot or a colon
-
- int dotx = path.indexOf('.');
- int colonx = path.indexOf(':');
-
- int contextStart = -1;
-
- if (dotx > 0)
- {
- logicalPageName = path.substring(1, dotx);
-
- int slashx = path.indexOf('/', dotx + 1);
-
- // The nested id ends at the colon (if present) or
- // the first slash (if present) or the end of the path.
-
- if (slashx < 0)
- {
- slashx = path.length();
- }
- else
- {
- contextStart = slashx + 1;
- }
-
- int nestedIdEnd = slashx;
-
- if (colonx > 0 && colonx < slashx)
- {
- nestedIdEnd = colonx;
- eventType = path.substring(colonx + 1, slashx);
- }
-
- nestedComponentId = path.substring(dotx + 1, nestedIdEnd);
- }
- else if (colonx > 0)
- {
- // No dot, but a colon. Therefore no nested component id, but an action name and
- // maybe some event context.
-
- int slashx = path.indexOf('/', colonx + 1);
- if (slashx < 0)
- {
- slashx = path.length();
- }
- else
- {
- contextStart = slashx + 1;
- }
-
- eventType = path.substring(colonx + 1, slashx);
- logicalPageName = path.substring(1, colonx);
- }
+ Matcher matcher = PATH_PATTERN.matcher(request.getPath());
+
+ if (!matcher.matches()) return false;
+
+ String logicalPageName = matcher.group(LOGICAL_PAGE_NAME);
- if (logicalPageName == null) return false;
+ String nestedComponentId = matcher.group(NESTED_ID);
- // We've identified a page name ... does the application contain a page with that name?
+ String eventType = matcher.group(EVENT_NAME);
+
+ if (nestedComponentId == null && eventType == null) return false;
if (!_componentClassResolver.isPageName(logicalPageName)) return false;
- String[] eventContext = contextStart > 0 ? decodeContext(path.substring(contextStart)) : _emptyString;
+ String[] eventContext = decodeContext(matcher.group(CONTEXT));
String activationContextValue = request.getParameter(InternalConstants.PAGE_CONTEXT_NAME);
String[] activationContext = activationContextValue == null ? _emptyString : decodeContext(
activationContextValue);
+ // The event type is often omitted, and defaults to "action".
+
+ if (eventType == null) eventType = TapestryConstants.ACTION_EVENT;
+
+ if (nestedComponentId == null) nestedComponentId = "";
+
_componentActionRequestHandler.handle(logicalPageName, nestedComponentId, eventType, eventContext,
activationContext);
@@ -123,7 +130,9 @@
private String[] decodeContext(String input)
{
- String[] result = input.split("/");
+ if (input == null) return _emptyString;
+
+ String[] result = SLASH_PATTERN.split(input);
for (int i = 0; i < result.length; i++)
{
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java Sat Dec 15 16:56:56 2007
@@ -30,14 +30,14 @@
* encoded by the current request (that is, bound to the current request's session, if any).
*
* @param component the component for which an action link is to be generated
- * @param action a name associated with the action
+ * @param eventType the type of event to trigger
* @param forForm true if the link is for a form, false otherwise
* @param context Additional path data, each value will be converted to a string and appended to the
* URI
* @return a link
+ * @see org.apache.tapestry.ComponentResourcesCommon#createActionLink(String, boolean, Object[])
*/
- Link createActionLink(ComponentPageElement component, String action, boolean forForm,
- Object... context);
+ Link createActionLink(ComponentPageElement component, String eventType, boolean forForm, Object... context);
/**
* Creates a render link for the page. If an activation context is supplied then that context is
@@ -52,7 +52,8 @@
* @param page the page to which a link should be created
* @param override if true, then the provided activation context is always used even if empty
* @param activationContext the activation context for the page
- * @return
+ * @return a link
+ * @see org.apache.tapestry.ComponentResourcesCommon#createPageLink(String, boolean, Object[])
*/
Link createPageLink(Page page, boolean override, Object... activationContext);
@@ -60,12 +61,12 @@
* As with {@link #createPageLink(Page, boolean, Object[])}, but the page is specified by
* logical name, rather than as an instance.
*
- * @param page the logical name of the page to generate a link to
- * @param override if true, then the provided activation context is always used even if empty
- * @param context activation context for the page
+ * @param logicalPageName the logical name of the page to generate a link to
+ * @param override if true, then the provided activation context is always used even if empty
+ * @param context activation context for the page
* @return
*/
- Link createPageLink(String page, boolean override, Object... context);
+ Link createPageLink(String logicalPageName, boolean override, Object... context);
/**
* Adds a listener, to be notified any time an action or render link is created; this allows the
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java Sat Dec 15 16:56:56 2007
@@ -103,15 +103,15 @@
_listeners.add(listener);
}
- public Link createActionLink(ComponentPageElement component, String action, boolean forForm, Object... context)
+ public Link createActionLink(ComponentPageElement component, String eventType, boolean forForm, Object... context)
{
- notBlank(action, "action");
+ notBlank(eventType, "action");
Page containingPage = component.getContainingPage();
String logicalPageName = containingPage.getLogicalName();
- ActionLinkTarget target = new ActionLinkTarget(action, logicalPageName, component
+ ActionLinkTarget target = new ActionLinkTarget(eventType, logicalPageName, component
.getNestedId());
String[] contextStrings = toContextStrings(context);
@@ -217,10 +217,10 @@
return result;
}
- public Link createPageLink(String pageName, boolean override, Object... context)
+ public Link createPageLink(String logicalPageName, boolean override, Object... context)
{
// This verifies that the page name is valid.
- Page page = _pageCache.get(pageName);
+ Page page = _pageCache.get(logicalPageName);
return createPageLink(page, override, context);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/TemplateParserImpl.java Sat Dec 15 16:56:56 2007
@@ -52,7 +52,7 @@
public static final String TAPESTRY_SCHEMA_5_0_0 = "http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";
- private static final String ID_REGEXP = "^[a-z]([a-z]|[0-9]|_)*$";
+ private static final String ID_REGEXP = "^[a-z]\\w*$";
private static final Pattern ID_PATTERN = Pattern.compile(ID_REGEXP, Pattern.CASE_INSENSITIVE);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/Page.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/Page.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/Page.java Sat Dec 15 16:56:56 2007
@@ -117,22 +117,14 @@
/**
* Creates a link that will trigger behavior in a component within the page.
*
- * @param element
- * @param action
- * @param forForm
- * @param context
- * @return
+ * @see org.apache.tapestry.ComponentResourcesCommon#createActionLink(String, boolean, Object[])
*/
- Link createActionLink(ComponentPageElement element, String action, boolean forForm,
- Object... context);
+ Link createActionLink(ComponentPageElement element, String eventType, boolean forForm, Object... context);
/**
* Creates a link to the named page.
*
- * @param pageName the logical name of the page
- * @param override if true, the provided context is used even if empty
- * @param context optional activation context for the page (if not provided, it is generated from
- * the page itself)
+ * @see org.apache.tapestry.ComponentResourcesCommon#createPageLink(String, boolean, Object[])
*/
Link createPageLink(String pageName, boolean override, Object... context);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java Sat Dec 15 16:56:56 2007
@@ -151,10 +151,9 @@
return _rootElement.getLogger();
}
- public Link createActionLink(ComponentPageElement element, String action, boolean forForm,
- Object... context)
+ public Link createActionLink(ComponentPageElement element, String eventType, boolean forForm, Object... context)
{
- return _linkFactory.createActionLink(element, action, forForm, context);
+ return _linkFactory.createActionLink(element, eventType, forForm, context);
}
public Link createPageLink(String pageName, boolean override, Object... context)
@@ -169,8 +168,7 @@
public Object getFieldChange(ComponentPageElement element, String fieldName)
{
- if (_fieldBundle == null)
- _fieldBundle = _persistentFieldManager.gatherChanges(_logicalPageName);
+ if (_fieldBundle == null) _fieldBundle = _persistentFieldManager.gatherChanges(_logicalPageName);
return _fieldBundle.getValue(element.getNestedId(), fieldName);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/ComponentActionDispatcherTest.java Sat Dec 15 16:56:56 2007
@@ -55,6 +55,24 @@
test("/foo/MyPage:anevent", "foo/MyPage", "", "anevent");
}
+ /**
+ * @see https://issues.apache.org/jira/browse/TAPESTRY-1949
+ */
+ @Test
+ public void event_on_page_with_name_and_dotted_parameters() throws Exception
+ {
+ test("/foo/MyPage:myevent/1.2.3/4.5.6", "foo/MyPage", "", "myevent", "1.2.3", "4.5.6");
+ }
+
+ /**
+ * @see https://issues.apache.org/jira/browse/TAPESTRY-1949
+ */
+ @Test
+ public void event_on_page_dotted_parameters() throws Exception
+ {
+ test("/foo/MyPage:action/1.2.3/4.5.6", "foo/MyPage", "", TapestryConstants.ACTION_EVENT, "1.2.3", "4.5.6");
+ }
+
@Test
public void event_on_component_within_page() throws Exception
{
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java?rev=604534&r1=604533&r2=604534&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java Sat Dec 15 16:56:56 2007
@@ -324,6 +324,49 @@
};
}
+ @Test
+ public void action_with_context_that_contains_periods()
+ {
+ Request request = mockRequest();
+ Response response = mockResponse();
+ ComponentPageElement element = mockComponentPageElement();
+ Page page = mockPage();
+ ComponentPageElement rootElement = mockComponentPageElement();
+ LinkFactoryListener listener = mockLinkFactoryListener();
+ ComponentInvocationMap map = mockComponentInvocationMap();
+ RequestPageCache cache = mockRequestPageCache();
+
+ final Holder<Link> holder = new Holder<Link>();
+
+ train_getContainingPage(element, page);
+ train_getLogicalName(page, "mypage");
+ train_getContextPath(request, "");
+ train_getNestedId(element, null);
+
+ train_getRootElement(page, rootElement);
+ train_triggerPassivateEventForActionLink(rootElement, listener, holder);
+
+ // This needs to be refactored a bit to be more testable.
+
+ map.store(isA(Link.class), isA(ComponentInvocation.class));
+
+ train_encodeURL(response, "/mypage:myaction/1.2.3/4.5.6?t:ac=foo/bar", ENCODED);
+
+ replay();
+
+ LinkFactory factory = new LinkFactoryImpl(request, response, map, cache, _typeCoercer);
+ factory.addListener(listener);
+
+ Link link = factory.createActionLink(element, "myaction", false, "1.2.3", "4.5.6");
+
+ assertEquals(link.toURI(), ENCODED);
+ assertSame(link, holder.get());
+
+ verify();
+
+ }
+
+
@SuppressWarnings("unchecked")
private void testActionLink(String contextPath, String logicalPageName, String nestedId, String eventName,
String expectedURI, Object... context)