From 1c3f601ba0e54c8f865a389f9af61fa3b1d01a53 Mon Sep 17 00:00:00 2001
From: anttil <anttil>
Date: Tue, 18 Sep 2012 15:26:57 +0000
Subject: [PATCH] SWE-2 / SP-263: A new layer created for GWT specific parts.

SVN: 26668
---
 .../ui/AbstractRegistrationForm.java          |   1 +
 .../PropertyTypeAssignmentForm.java           |  39 +++---
 ...actDataListDeletionConfirmationDialog.java |   1 +
 .../cisd/openbis/uitest/ManualTest.java       |   2 +-
 .../uitest/infra/ApplicationRunner.java       |   5 -
 .../cisd/openbis/uitest/infra/Locate.java     |  29 ++++
 .../cisd/openbis/uitest/infra/PageProxy.java  |  67 ++++++---
 .../openbis/uitest/infra/WebElementProxy.java |  61 +++++++++
 .../cisd/openbis/uitest/page/HomePage.java    |  15 +-
 .../cisd/openbis/uitest/page/LoginPage.java   |  24 ++--
 .../openbis/uitest/page/NavigationPage.java   |  49 +++----
 .../cisd/openbis/uitest/page/Page.java        |  46 -------
 .../page/dialog/AddExperimentTypeDialog.java  |  31 +++--
 .../page/dialog/AddSampleTypeDialog.java      |  85 +++++-------
 .../uitest/page/dialog/AddSpaceDialog.java    |  18 +--
 .../page/dialog/AddVocabularyDialog.java      |  41 +++---
 .../page/dialog/EditSampleTypeDialog.java     | 105 ++++++--------
 .../page/dialog/InvalidPasswordDialog.java    |  11 +-
 .../openbis/uitest/page/menu/AdminMenu.java   |  63 +++------
 .../uitest/page/menu/AuthorizationMenu.java   |   9 +-
 .../openbis/uitest/page/menu/BrowseMenu.java  |  17 ++-
 .../uitest/page/menu/MetadataMenu.java        |  21 ++-
 .../openbis/uitest/page/menu/NewMenu.java     |  17 ++-
 .../openbis/uitest/page/menu/TypesMenu.java   |  45 ++++++
 .../openbis/uitest/page/menu/UserMenu.java    |  11 +-
 .../uitest/page/tab/AddPropertyType.java      |  61 ++++-----
 .../page/tab/AssignSamplePropertyType.java    |  85 ++++++------
 .../uitest/page/tab/ExperimentBrowser.java    |  45 ++----
 .../page/tab/ExperimentTypeBrowser.java       |  38 ++----
 .../uitest/page/tab/ProjectBrowser.java       |  28 ++--
 .../tab/PropertyTypeAssignmentBrowser.java    |  33 ++---
 .../uitest/page/tab/PropertyTypeBrowser.java  |  32 ++---
 .../uitest/page/tab/RegisterExperiment.java   |  53 +++----
 .../uitest/page/tab/RegisterProject.java      |  35 ++---
 .../uitest/page/tab/RegisterSample.java       | 129 +++++++-----------
 .../page/tab/RoleAssignmentBrowser.java       |  27 +---
 .../uitest/page/tab/SampleBrowser.java        |  77 +++--------
 .../uitest/page/tab/SampleTypeBrowser.java    |  51 +++----
 .../openbis/uitest/page/tab/SpaceBrowser.java |  34 ++---
 .../cisd/openbis/uitest/page/tab/Trash.java   |  19 +--
 .../uitest/page/tab/VocabularyBrowser.java    |  35 ++---
 .../AlertMessageBox.java}                     |  16 +--
 .../cisd/openbis/uitest/widget/Button.java    |  28 ++++
 .../cisd/openbis/uitest/widget/Checkbox.java  |  41 ++++++
 .../widget/DeletionConfirmationBox.java       |  42 ++++++
 .../cisd/openbis/uitest/widget/DropDown.java  |  74 ++++++++++
 .../cisd/openbis/uitest/widget/Fillable.java  |  25 ++++
 .../cisd/openbis/uitest/widget/Form.java      |  58 ++++++++
 .../cisd/openbis/uitest/widget/Grid.java      |  62 +++++++++
 .../cisd/openbis/uitest/widget/Link.java      |  38 ++++++
 .../cisd/openbis/uitest/widget/Text.java      |  61 +++++++++
 .../cisd/openbis/uitest/widget/TextArea.java  |  52 +++++++
 .../cisd/openbis/uitest/widget/TreeGrid.java  |  52 +++++++
 .../cisd/openbis/uitest/widget/Widget.java    |  54 ++++++++
 54 files changed, 1293 insertions(+), 905 deletions(-)
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/Locate.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/WebElementProxy.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/TypesMenu.java
 rename ui-test/source/java/ch/systemsx/cisd/openbis/uitest/{page/Fragment.java => widget/AlertMessageBox.java} (72%)
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Button.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Checkbox.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DeletionConfirmationBox.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DropDown.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Fillable.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Form.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Grid.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Link.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Text.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TextArea.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TreeGrid.java
 create mode 100644 ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Widget.java

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractRegistrationForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractRegistrationForm.java
index 5005ea475b8..661f8ff5911 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractRegistrationForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/AbstractRegistrationForm.java
@@ -128,6 +128,7 @@ public abstract class AbstractRegistrationForm extends ContentPanel implements
         add(infoBox = createInfoBox());
         add(loadingInfo = createLoadingInfo());
         add(WidgetUtils.inRow(formPanel = createFormPanel(), rightPanel = createAdditionalPanel()));
+        formPanel.setId("registration-panel-" + id);
         add(unsavedChangesInfo = createUnsavedChangesInfo());
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/property_type/PropertyTypeAssignmentForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/property_type/PropertyTypeAssignmentForm.java
index 70865db7cc7..dc87e5eacbf 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/property_type/PropertyTypeAssignmentForm.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/property_type/PropertyTypeAssignmentForm.java
@@ -20,6 +20,24 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
+import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
+import com.extjs.gxt.ui.client.Style.Orientation;
+import com.extjs.gxt.ui.client.Style.Scroll;
+import com.extjs.gxt.ui.client.event.BaseEvent;
+import com.extjs.gxt.ui.client.event.ButtonEvent;
+import com.extjs.gxt.ui.client.event.Events;
+import com.extjs.gxt.ui.client.event.Listener;
+import com.extjs.gxt.ui.client.event.SelectionListener;
+import com.extjs.gxt.ui.client.widget.LayoutContainer;
+import com.extjs.gxt.ui.client.widget.button.Button;
+import com.extjs.gxt.ui.client.widget.form.CheckBox;
+import com.extjs.gxt.ui.client.widget.form.Field;
+import com.extjs.gxt.ui.client.widget.form.FormPanel;
+import com.extjs.gxt.ui.client.widget.form.Radio;
+import com.extjs.gxt.ui.client.widget.form.RadioGroup;
+import com.extjs.gxt.ui.client.widget.layout.FlowLayout;
+import com.google.gwt.user.client.Element;
+
 import ch.systemsx.cisd.openbis.generic.client.web.client.ICommonClientServiceAsync;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict;
@@ -52,24 +70,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewETPTAssignment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ScriptType;
 
-import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
-import com.extjs.gxt.ui.client.Style.Orientation;
-import com.extjs.gxt.ui.client.Style.Scroll;
-import com.extjs.gxt.ui.client.event.BaseEvent;
-import com.extjs.gxt.ui.client.event.ButtonEvent;
-import com.extjs.gxt.ui.client.event.Events;
-import com.extjs.gxt.ui.client.event.Listener;
-import com.extjs.gxt.ui.client.event.SelectionListener;
-import com.extjs.gxt.ui.client.widget.LayoutContainer;
-import com.extjs.gxt.ui.client.widget.button.Button;
-import com.extjs.gxt.ui.client.widget.form.CheckBox;
-import com.extjs.gxt.ui.client.widget.form.Field;
-import com.extjs.gxt.ui.client.widget.form.FormPanel;
-import com.extjs.gxt.ui.client.widget.form.Radio;
-import com.extjs.gxt.ui.client.widget.form.RadioGroup;
-import com.extjs.gxt.ui.client.widget.layout.FlowLayout;
-import com.google.gwt.user.client.Element;
-
 /**
  * The property type assignment panel.
  * 
@@ -591,8 +591,7 @@ public final class PropertyTypeAssignmentForm extends LayoutContainer implements
         if (propertyType != null)
         {
             String fieldId =
-                    createChildId(DEFAULT_VALUE_ID_PART
-                            + GWTUtils.escapeToFormId(propertyType.getSimpleCode()));
+                    createChildId(DEFAULT_VALUE_ID_PART);
             DatabaseModificationAwareField<?> fieldHolder =
                     PropertyFieldFactory.createField(propertyType, false,
                             viewContext.getMessage(Dict.DEFAULT_VALUE), fieldId, null, viewContext);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/AbstractDataListDeletionConfirmationDialog.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/AbstractDataListDeletionConfirmationDialog.java
index d7fbc40c86e..4f5fa2ccfdd 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/AbstractDataListDeletionConfirmationDialog.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/widget/AbstractDataListDeletionConfirmationDialog.java
@@ -81,6 +81,7 @@ public abstract class AbstractDataListDeletionConfirmationDialog<T> extends
         formPanel.setFieldWidth(FIELD_WIDTH);
 
         reason = new ReasonField(messageProvider, true);
+        reason.setId("deletion-reason");
         reason.focus();
         reason.addKeyListener(keyListener);
         if (withRadio)
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/ManualTest.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/ManualTest.java
index bcf9a9dc89b..658958bcbde 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/ManualTest.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/ManualTest.java
@@ -40,7 +40,7 @@ import ch.systemsx.cisd.openbis.uitest.type.Vocabulary;
 public class ManualTest extends SeleniumTest
 {
 
-    @Test
+    @Test(enabled = false)
     public void basic()
     {
         // 0) Cleanup
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/ApplicationRunner.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/ApplicationRunner.java
index d651b8232b6..d45c7778687 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/ApplicationRunner.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/ApplicationRunner.java
@@ -261,11 +261,6 @@ public class ApplicationRunner
         return getMenus().newMenu().project();
     }
 
-    public void closeAllTabs()
-    {
-        getMenus().closeTabs();
-    }
-
     private NavigationPage getMenus()
     {
         return proxy.get(NavigationPage.class);
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/Locate.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/Locate.java
new file mode 100644
index 00000000000..89b632f2ece
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/Locate.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.infra;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Locate
+{
+    public abstract String value();
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/PageProxy.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/PageProxy.java
index 4020e470cd9..709146b1c76 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/PageProxy.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/PageProxy.java
@@ -19,17 +19,17 @@ package ch.systemsx.cisd.openbis.uitest.infra;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.Collection;
+import java.lang.reflect.Modifier;
 
 import javassist.util.proxy.MethodHandler;
 import javassist.util.proxy.ProxyFactory;
 
+import org.openqa.selenium.By;
 import org.openqa.selenium.StaleElementReferenceException;
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.PageFactory;
 
 import ch.systemsx.cisd.openbis.uitest.page.Page;
+import ch.systemsx.cisd.openbis.uitest.widget.Widget;
 
 /**
  * @author anttil
@@ -44,7 +44,7 @@ public class PageProxy
     }
 
     @SuppressWarnings("unchecked")
-    public <T extends Page> T get(Class<T> clazz)
+    public <T extends Page> T get(final Class<T> clazz)
     {
 
         ProxyFactory factory = new ProxyFactory();
@@ -63,7 +63,7 @@ public class PageProxy
                     {
                         if (e.getTargetException() instanceof StaleElementReferenceException)
                         {
-                            PageFactory.initElements(new ScreenShotDecorator(shotter), self);
+                            initLocateFields(clazz, (T) self);
                             return proceed.invoke(self, args);
                         } else
                         {
@@ -94,44 +94,69 @@ public class PageProxy
             throw new RuntimeException(ex1);
         }
 
-        PageFactory.initElements(new ScreenShotDecorator(shotter), t);
         t.setPageProxy(this);
-        t.setScreenShotter(shotter);
 
+        initLocateFields(clazz, t);
+
+        return t;
+    }
+
+    private <T> void initLocateFields(Class<T> clazz, T t)
+    {
         Class<T> pageClass = clazz;
         while (pageClass != null)
         {
             for (Field field : pageClass.getDeclaredFields())
             {
-                if ((field.getAnnotation(FindBy.class) != null)
-                        && (field.getAnnotation(NotAlwaysPresent.class) == null))
+                Locate locate = field.getAnnotation(Locate.class);
+                if (locate != null)
                 {
-                    WebElement element = null;
                     try
                     {
                         field.setAccessible(true);
-                        Object potentialWebElement = field.get(t);
-                        if (potentialWebElement instanceof Collection)
+                        Class<?> type = field.getType();
+
+                        Widget widget;
+                        if (Modifier.isAbstract(type.getModifiers()))
                         {
-                            continue;
+                            widget = new Widget()
+                                {
+                                };
+                        } else
+                        {
+                            widget = (Widget) type.newInstance();
                         }
-                        element = (WebElement) potentialWebElement;
+
+                        WebElement element;
+                        if (field.getAnnotation(NotAlwaysPresent.class) != null)
+                        {
+                            element = (WebElement) WebElementProxy.newInstance(locate.value());
+                        } else
+                        {
+                            element = SeleniumTest.driver.findElement(By.id(locate.value()));
+                        }
+                        widget.setContext((WebElement) ScreenShotProxy
+                                .newInstance(element, shotter));
+                        field.set(t, widget);
+                    } catch (IllegalArgumentException ex)
+                    {
+                        // TODO Auto-generated catch block
+                        ex.printStackTrace();
+                        throw ex;
                     } catch (IllegalAccessException ex)
                     {
                         ex.printStackTrace();
                         throw new RuntimeException(ex);
+                    } catch (InstantiationException ex)
+                    {
+                        // TODO Auto-generated catch block
+                        ex.printStackTrace();
+                        throw new RuntimeException(ex);
                     }
-
-                    // Force wait for the element.
-                    // This makes sure that page object is returned only when all the
-                    // expected elements are present.
-                    element.getTagName();
                 }
             }
 
             pageClass = (Class<T>) pageClass.getSuperclass();
         }
-
-        return t;
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/WebElementProxy.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/WebElementProxy.java
new file mode 100644
index 00000000000..cee4b78a7b2
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/infra/WebElementProxy.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.infra;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+public class WebElementProxy implements InvocationHandler
+{
+
+    private String id;
+
+    public static Object newInstance(String id)
+    {
+        return java.lang.reflect.Proxy.newProxyInstance(
+                WebElement.class.getClassLoader(),
+                new Class<?>[]
+                    { WebElement.class },
+                new WebElementProxy(id));
+    }
+
+    private WebElementProxy(String id)
+    {
+        this.id = id;
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
+    {
+        try
+        {
+            WebElement e = SeleniumTest.driver.findElement(By.id(id));
+            return m.invoke(e, args);
+        } catch (InvocationTargetException e)
+        {
+            throw e.getTargetException();
+        } catch (Exception e)
+        {
+            throw new RuntimeException("unexpected invocation exception: " +
+                    e.getMessage());
+        }
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/HomePage.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/HomePage.java
index 7d34b607051..c0c3a8be582 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/HomePage.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/HomePage.java
@@ -16,19 +16,12 @@
 
 package ch.systemsx.cisd.openbis.uitest.page;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
-import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
 
 public class HomePage extends NavigationPage
 {
 
-    @NotAlwaysPresent
-    @FindBy(id = "just_something_that_is_not_there")
-    private WebElement nonExistent;
-
-    @FindBy(id = "openbis_search-widget_text-field-input")
-    private WebElement searchTextBox;
-
+    @Locate("openbis_search-widget_text-field-input")
+    private Text searchTextBox;
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/LoginPage.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/LoginPage.java
index 9e40f8e6fe1..964358a6e3e 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/LoginPage.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/LoginPage.java
@@ -16,28 +16,26 @@
 
 package ch.systemsx.cisd.openbis.uitest.page;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
 
 public class LoginPage extends Page
 {
 
-    @FindBy(id = "openbis_login_username")
-    private WebElement username;
+    @Locate("openbis_login_username")
+    private Text username;
 
-    @FindBy(id = "openbis_login_password")
-    private WebElement password;
+    @Locate("openbis_login_password")
+    private Text password;
 
-    @FindBy(id = "openbis_login_submit")
-    private WebElement button;
+    @Locate("openbis_login_submit")
+    private Button button;
 
     public void loginAs(String user, String pwd)
     {
-        username.clear();
-        password.clear();
-
-        username.sendKeys(user);
-        password.sendKeys(pwd);
+        username.write(user);
+        password.write(pwd);
 
         button.click();
     }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/NavigationPage.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/NavigationPage.java
index 549a04d7d7d..e92ee0624aa 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/NavigationPage.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/NavigationPage.java
@@ -16,55 +16,49 @@
 
 package ch.systemsx.cisd.openbis.uitest.page;
 
-import java.util.List;
-
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.menu.AdminMenu;
 import ch.systemsx.cisd.openbis.uitest.page.menu.BrowseMenu;
 import ch.systemsx.cisd.openbis.uitest.page.menu.NewMenu;
 import ch.systemsx.cisd.openbis.uitest.page.menu.UserMenu;
 import ch.systemsx.cisd.openbis.uitest.page.tab.Trash;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
 
 /**
  * @author anttil
  */
 public abstract class NavigationPage extends Page
 {
-    @FindBy(id = "admin_menu")
-    private WebElement adminMenuButton;
-
-    @FindBy(id = "user_menu")
-    private WebElement userMenuButton;
+    @Locate("admin_menu")
+    private Button adminMenu;
 
-    @FindBy(id = "browse_menu")
-    private WebElement browseMenuButton;
+    @Locate("user_menu")
+    private Button userMenu;
 
-    @FindBy(id = "new_menu")
-    private WebElement newMenuButton;
+    @Locate("browse_menu")
+    private Button browseMenu;
 
-    @FindBy(id = "trash-button")
-    private WebElement trash;
+    @Locate("new_menu")
+    private Button newMenu;
 
-    @FindBy(className = "x-tab-strip-close")
-    private List<WebElement> tabCloseButtons;
+    @Locate("trash-button")
+    private Button trash;
 
     public AdminMenu admin()
     {
-        adminMenuButton.click();
+        adminMenu.click();
         return get(AdminMenu.class);
     }
 
     public UserMenu user()
     {
-        userMenuButton.click();
+        userMenu.click();
         return get(UserMenu.class);
     }
 
     public BrowseMenu browse()
     {
-        browseMenuButton.click();
+        browseMenu.click();
         return get(BrowseMenu.class);
     }
 
@@ -76,19 +70,8 @@ public abstract class NavigationPage extends Page
 
     public NewMenu newMenu()
     {
-        newMenuButton.click();
+        newMenu.click();
         return get(NewMenu.class);
     }
 
-    public void closeTabs()
-    {
-        for (WebElement e : tabCloseButtons)
-        {
-            if (e.isDisplayed())
-            {
-                e.click();
-            }
-        }
-    }
-
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/Page.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/Page.java
index 19d1894aba5..7e1ec8ded1d 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/Page.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/Page.java
@@ -16,20 +16,14 @@
 
 package ch.systemsx.cisd.openbis.uitest.page;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.interactions.Actions;
 import org.openqa.selenium.support.ui.ExpectedCondition;
 import org.openqa.selenium.support.ui.WebDriverWait;
 
@@ -62,22 +56,6 @@ public abstract class Page
         return this.pageProxy.get(clazz);
     }
 
-    public WebElement findElementWithText(String text, By by)
-    {
-        WebElement element = null;
-        for (WebElement e : SeleniumTest.driver.findElements(by))
-        {
-            if (e.getText().equals(text))
-            {
-                element = e;
-                break;
-            }
-        }
-        assertThat(element, is(notNullValue()));
-
-        return (WebElement) ScreenShotProxy.newInstance(element, shotter);
-    }
-
     public WebElement findElement(WebElement element, String xpath)
     {
         return (WebElement) ScreenShotProxy.newInstance(element.findElement(By.xpath(xpath)),
@@ -131,28 +109,4 @@ public abstract class Page
 
             });
     }
-
-    protected void select(Collection<? extends WebElement> choices, String text)
-    {
-        Collection<String> found = new HashSet<String>();
-        for (WebElement choice : choices)
-        {
-            if (choice.getText().equalsIgnoreCase(text))
-            {
-                Actions builder = new Actions(SeleniumTest.driver);
-                builder.moveToElement(choice).click(choice).build().perform();
-                return;
-            }
-            found.add(choice.getText());
-        }
-        throw new IllegalArgumentException("Selection " + text + " not found, got " + found);
-    }
-
-    protected void checkbox(WebElement box, boolean check)
-    {
-        if (box.getAttribute("checked") != null ^ check)
-        {
-            box.click();
-        }
-    }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddExperimentTypeDialog.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddExperimentTypeDialog.java
index abb2da32c1c..a4dc727c185 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddExperimentTypeDialog.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddExperimentTypeDialog.java
@@ -16,44 +16,45 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.dialog;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.ExperimentTypeBrowser;
 import ch.systemsx.cisd.openbis.uitest.type.ExperimentType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
+import ch.systemsx.cisd.openbis.uitest.widget.TextArea;
 
 public class AddExperimentTypeDialog extends Page
 {
 
-    @FindBy(id = "openbis_dialog-code-field-input")
-    private WebElement code;
+    @Locate("openbis_dialog-code-field")
+    private Text code;
 
-    @FindBy(id = "openbis_add-type-dialog-description-field-input")
-    private WebElement description;
+    @Locate("openbis_add-type-dialog-description-field")
+    private TextArea description;
 
-    @FindBy(id = "openbis_dialog-save-button")
-    private WebElement saveButton;
+    @Locate("openbis_dialog-save-button")
+    private Button save;
 
-    @FindBy(id = "openbis_dialog-cancel-button")
-    private WebElement cancelButton;
+    @Locate("openbis_dialog-cancel-button")
+    private Button cancel;
 
     public ExperimentTypeBrowser save()
     {
-        saveButton.click();
+        save.click();
         return get(ExperimentTypeBrowser.class);
     }
 
     public ExperimentTypeBrowser cancel()
     {
-        cancelButton.click();
+        cancel.click();
         return get(ExperimentTypeBrowser.class);
     }
 
     public void fillWith(ExperimentType experimentType)
     {
-        this.code.sendKeys(experimentType.getCode());
-        this.description.sendKeys(experimentType.getDescription());
+        code.write(experimentType.getCode());
+        description.write(experimentType.getDescription());
     }
 
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSampleTypeDialog.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSampleTypeDialog.java
index e095161ac0a..8a5b6d5b9fd 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSampleTypeDialog.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSampleTypeDialog.java
@@ -16,104 +16,83 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.dialog;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.SampleTypeBrowser;
 import ch.systemsx.cisd.openbis.uitest.type.SampleType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Checkbox;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
 
 public class AddSampleTypeDialog extends Page
 {
 
-    @FindBy(id = "openbis_dialog-code-field-input")
-    private WebElement code;
-
-    @FindBy(id = "openbis_add-type-dialog-description-field-input")
-    private WebElement description;
+    @Locate("openbis_dialog-code-field")
+    private Text code;
 
-    // @FindBy(id = "")
-    private WebElement validationScript;
+    @Locate("openbis_add-type-dialog-description-field")
+    private Text description;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-listable"),
-                @FindBy(xpath = "input") })
-    private WebElement listable;
+    @Locate("openbis_add-type-dialog-listable")
+    private Checkbox listable;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-show-container"),
-                @FindBy(xpath = "input") })
-    private WebElement showContainer;
+    @Locate("openbis_add-type-dialog-show-container")
+    private Checkbox showContainer;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-show-parents"),
-                @FindBy(xpath = "input") })
-    private WebElement showParents;
+    @Locate("openbis_add-type-dialog-show-parents")
+    private Checkbox showParents;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-subcode-unique"),
-                @FindBy(xpath = "input") })
-    private WebElement uniqueSubcodes;
+    @Locate("openbis_add-type-dialog-subcode-unique")
+    private Checkbox uniqueSubcodes;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-autogenerated-code"),
-                @FindBy(xpath = "input") })
-    private WebElement generateCodesAutomatically;
+    @Locate("openbis_add-type-dialog-autogenerated-code")
+    private Checkbox generateCodesAutomatically;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-show-parent-metadata"),
-                @FindBy(xpath = "input") })
-    private WebElement showParentMetadata;
+    @Locate("openbis_add-type-dialog-show-parent-metadata")
+    private Checkbox showParentMetadata;
 
-    @FindBy(id = "openbis_add-type-dialog-generated-code-prefix")
-    private WebElement generatedCodePrefix;
+    @Locate("openbis_add-type-dialog-generated-code-prefix")
+    private Text generatedCodePrefix;
 
-    @FindBy(id = "openbis_dialog-save-button")
-    private WebElement saveButton;
+    @Locate("openbis_dialog-save-button")
+    private Button save;
 
-    @FindBy(id = "openbis_dialog-cancel-button")
-    private WebElement cancelButton;
+    @Locate("openbis_dialog-cancel-button")
+    private Button cancel;
 
     public SampleTypeBrowser save()
     {
-        saveButton.click();
+        save.click();
         return get(SampleTypeBrowser.class);
     }
 
     public SampleTypeBrowser cancel()
     {
-        cancelButton.click();
+        cancel.click();
         return get(SampleTypeBrowser.class);
     }
 
     public AddSampleTypeDialog setCode(String code)
     {
-        this.code.sendKeys(code);
+        this.code.write(code);
         return this;
     }
 
     public AddSampleTypeDialog setListable(boolean checked)
     {
-        checkbox(this.listable, checked);
+        listable.set(checked);
         return this;
     }
 
     public AddSampleTypeDialog setShowContainer(boolean checked)
     {
-        checkbox(this.showContainer, checked);
+        showContainer.set(checked);
         return this;
     }
 
     public AddSampleTypeDialog setShowParents(boolean checked)
     {
-        checkbox(this.showParents, checked);
+        showParents.set(checked);
         return this;
     }
 
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSpaceDialog.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSpaceDialog.java
index d48036d5a90..9c1e95c79ac 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSpaceDialog.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddSpaceDialog.java
@@ -16,21 +16,21 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.dialog;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.SpaceBrowser;
 import ch.systemsx.cisd.openbis.uitest.type.Space;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
 
 public class AddSpaceDialog extends Page
 {
 
-    @FindBy(id = "openbis_dialog-code-field-input")
-    private WebElement code;
+    @Locate("openbis_dialog-code-field")
+    private Text code;
 
-    @FindBy(id = "openbis_dialog-save-button")
-    private WebElement saveButton;
+    @Locate("openbis_dialog-save-button")
+    private Button save;
 
     /* this is not deleted as this is an example of WAIT
     public SpaceBrowser addSpace(String name, String description)
@@ -43,12 +43,12 @@ public class AddSpaceDialog extends Page
     */
     public void fillWith(Space space)
     {
-        this.code.sendKeys(space.getCode());
+        code.write(space.getCode());
     }
 
     public SpaceBrowser save()
     {
-        this.saveButton.click();
+        save.click();
         return get(SpaceBrowser.class);
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddVocabularyDialog.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddVocabularyDialog.java
index f541facf274..619221544e1 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddVocabularyDialog.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/AddVocabularyDialog.java
@@ -16,48 +16,49 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.dialog;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.VocabularyBrowser;
 import ch.systemsx.cisd.openbis.uitest.type.Vocabulary;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
+import ch.systemsx.cisd.openbis.uitest.widget.TextArea;
 
 public class AddVocabularyDialog extends Page
 {
 
-    @FindBy(id = "openbis_vocabulary-registration_formvocabulary_registration_field_set_code-input")
-    private WebElement code;
+    @Locate("openbis_vocabulary-registration_formvocabulary_registration_field_set_code")
+    private Text code;
 
-    @FindBy(id = "openbis_vocabulary-registration_formvocabulary_registration_field_set_description-input")
-    private WebElement description;
+    @Locate("openbis_vocabulary-registration_formvocabulary_registration_field_set_description")
+    private TextArea description;
 
-    @FindBy(id = "openbis_vocabulary-registration_formvocabulary_registration_field_set_terms-input")
-    private WebElement terms;
+    @Locate("openbis_vocabulary-registration_formvocabulary_registration_field_set_terms")
+    private TextArea terms;
 
-    @FindBy(id = "vocabulary_registration_field_set-url-input")
-    private WebElement url;
+    @Locate("vocabulary_registration_field_set-url")
+    private Text url;
 
-    @FindBy(id = "openbis_vocabulary-registration_formsave-button")
-    private WebElement saveButton;
+    @Locate("openbis_vocabulary-registration_formsave-button")
+    private Button save;
 
     public void fillWith(Vocabulary vocabulary)
     {
-        this.code.sendKeys(vocabulary.getCode());
-        this.description.sendKeys(vocabulary.getDescription());
+        code.write(vocabulary.getCode());
+        description.write(vocabulary.getDescription());
 
-        this.terms.clear();
+        terms.clear();
         for (String term : vocabulary.getTerms())
         {
-            this.terms.sendKeys(term + ", ");
+            terms.append(term + ", ");
         }
-        this.url.clear();
-        this.url.sendKeys(vocabulary.getUrl());
+
+        url.write(vocabulary.getUrl());
     }
 
     public VocabularyBrowser save()
     {
-        this.saveButton.click();
+        save.click();
         return get(VocabularyBrowser.class);
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/EditSampleTypeDialog.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/EditSampleTypeDialog.java
index 37b1bcedfca..5a2941ee66d 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/EditSampleTypeDialog.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/EditSampleTypeDialog.java
@@ -16,106 +16,77 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.dialog;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.SampleTypeBrowser;
 import ch.systemsx.cisd.openbis.uitest.type.SampleType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Checkbox;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
 
 public class EditSampleTypeDialog extends Page
 {
 
-    @FindBy(id = "openbis_edit-type-dialog-description-input")
-    private WebElement description;
-
-    // @FindBy(id = "")
-    private WebElement validationScript;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-listable"),
-                @FindBy(xpath = "input") })
-    private WebElement listable;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-show-container"),
-                @FindBy(xpath = "input") })
-    private WebElement showContainer;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-show-parents"),
-                @FindBy(xpath = "input") })
-    private WebElement showParents;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-subcode-unique"),
-                @FindBy(xpath = "input") })
-    private WebElement uniqueSubcodes;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-autogenerated-code"),
-                @FindBy(xpath = "input") })
-    private WebElement generateCodesAutomatically;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_add-type-dialog-show-parent-metadata"),
-                @FindBy(xpath = "input") })
-    private WebElement showParentMetadata;
-
-    @FindBy(id = "openbis_add-type-dialog-generated-code-prefix")
-    private WebElement generatedCodePrefix;
-
-    @FindBy(id = "openbis_dialog-save-button")
-    private WebElement saveButton;
-
-    @FindBy(id = "openbis_dialog-cancel-button")
-    private WebElement cancelButton;
+    @Locate("openbis_edit-type-dialog-description")
+    private Text description;
+
+    @Locate("openbis_add-type-dialog-listable")
+    private Checkbox listable;
+
+    @Locate("openbis_add-type-dialog-show-container")
+    private Checkbox showContainer;
+
+    @Locate("openbis_add-type-dialog-show-parents")
+    private Checkbox showParents;
+
+    @Locate("openbis_add-type-dialog-subcode-unique")
+    private Checkbox uniqueSubcodes;
+
+    @Locate("openbis_add-type-dialog-autogenerated-code")
+    private Checkbox generateCodesAutomatically;
+
+    @Locate("openbis_add-type-dialog-show-parent-metadata")
+    private Checkbox showParentMetadata;
+
+    @Locate("openbis_add-type-dialog-generated-code-prefix")
+    private Text generatedCodePrefix;
+
+    @Locate("openbis_dialog-save-button")
+    private Button save;
+
+    @Locate("openbis_dialog-cancel-button")
+    private Button cancel;
 
     public SampleTypeBrowser save()
     {
-        saveButton.click();
+        save.click();
         return get(SampleTypeBrowser.class);
     }
 
     public SampleTypeBrowser cancel()
     {
-        cancelButton.click();
+        cancel.click();
         return get(SampleTypeBrowser.class);
     }
 
     public EditSampleTypeDialog setListable(boolean checked)
     {
-        setCheckBox(this.listable, checked);
+        listable.set(checked);
         return this;
     }
 
     public EditSampleTypeDialog setShowContainer(boolean checked)
     {
-        setCheckBox(this.showContainer, checked);
+        showContainer.set(checked);
         return this;
     }
 
     public EditSampleTypeDialog setShowParents(boolean checked)
     {
-        setCheckBox(this.showParents, checked);
+        showParents.set(checked);
         return this;
     }
 
-    private void setCheckBox(WebElement element, boolean checked)
-    {
-        if (element.getAttribute("checked") != null ^ checked)
-        {
-            element.click();
-        }
-    }
-
     public void fillWith(SampleType sampleType)
     {
         setListable(sampleType.isListable());
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/InvalidPasswordDialog.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/InvalidPasswordDialog.java
index 1ec0e486be8..ee4ae0ae08f 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/InvalidPasswordDialog.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/dialog/InvalidPasswordDialog.java
@@ -16,21 +16,20 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.dialog;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.LoginPage;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
+import ch.systemsx.cisd.openbis.uitest.widget.AlertMessageBox;
 
 public class InvalidPasswordDialog extends Page
 {
 
-    @FindBy(id = "login_failed_dialog")
-    private WebElement errorDialog;
+    @Locate("login_failed_dialog")
+    private AlertMessageBox errorDialog;
 
     public LoginPage dismiss()
     {
-        findElement(errorDialog, "//button[text()=\"OK\"]").click();
+        errorDialog.dismiss();
         return get(LoginPage.class);
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AdminMenu.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AdminMenu.java
index 8142ede00d8..bbde95f7486 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AdminMenu.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AdminMenu.java
@@ -16,43 +16,29 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.menu;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.interactions.Actions;
-import org.openqa.selenium.support.FindBy;
-
-import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
-import ch.systemsx.cisd.openbis.uitest.infra.SeleniumTest;
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
-import ch.systemsx.cisd.openbis.uitest.page.tab.ExperimentTypeBrowser;
-import ch.systemsx.cisd.openbis.uitest.page.tab.SampleTypeBrowser;
 import ch.systemsx.cisd.openbis.uitest.page.tab.SpaceBrowser;
 import ch.systemsx.cisd.openbis.uitest.page.tab.VocabularyBrowser;
+import ch.systemsx.cisd.openbis.uitest.widget.Link;
 
 public class AdminMenu extends Page
 {
 
-    @FindBy(id = "openbis_top-menu_ADMINISTRATION_MENU_MANAGE_GROUPS")
-    private WebElement spaces;
-
-    @FindBy(id = "openbis_top-menu_VOCABULARY_MENU_BROWSE")
-    private WebElement vocabularies;
-
-    @FindBy(id = "ADMINISTRATION_MENU_MANAGE_TYPES")
-    private WebElement types;
+    @Locate("openbis_top-menu_ADMINISTRATION_MENU_MANAGE_GROUPS")
+    private Link spaces;
 
-    @FindBy(id = "ADMINISTRATION_MENU_MANAGE_PROPERTY_TYPES")
-    private WebElement metadata;
+    @Locate("openbis_top-menu_VOCABULARY_MENU_BROWSE")
+    private Link vocabularies;
 
-    @NotAlwaysPresent
-    @FindBy(id = "openbis_top-menu_SAMPLE_MENU_TYPES")
-    private WebElement sampleTypes;
+    @Locate("ADMINISTRATION_MENU_MANAGE_TYPES")
+    private Link types;
 
-    @NotAlwaysPresent
-    @FindBy(id = "openbis_top-menu_EXPERIMENT_MENU_TYPES")
-    private WebElement experimentTypes;
+    @Locate("ADMINISTRATION_MENU_MANAGE_PROPERTY_TYPES")
+    private Link metadata;
 
-    @FindBy(id = "ADMINISTRATION_MENU_MANAGE_AUTHORIZATION")
-    private WebElement authorization;
+    @Locate("ADMINISTRATION_MENU_MANAGE_AUTHORIZATION")
+    private Link authorization;
 
     public SpaceBrowser spaces()
     {
@@ -66,36 +52,21 @@ public class AdminMenu extends Page
         return get(VocabularyBrowser.class);
     }
 
-    public AdminMenu types()
+    public TypesMenu types()
     {
-        Actions builder = new Actions(SeleniumTest.driver);
-        builder.moveToElement(types).build().perform();
-        return get(AdminMenu.class);
+        types.highlight();
+        return get(TypesMenu.class);
     }
 
     public MetadataMenu metadata()
     {
-        Actions builder = new Actions(SeleniumTest.driver);
-        builder.moveToElement(metadata).build().perform();
+        metadata.highlight();
         return get(MetadataMenu.class);
     }
 
-    public SampleTypeBrowser sampleTypes()
-    {
-        sampleTypes.click();
-        return get(SampleTypeBrowser.class);
-    }
-
-    public ExperimentTypeBrowser experimentTypes()
-    {
-        experimentTypes.click();
-        return get(ExperimentTypeBrowser.class);
-    }
-
     public AuthorizationMenu authorization()
     {
-        Actions builder = new Actions(SeleniumTest.driver);
-        builder.moveToElement(authorization).build().perform();
+        authorization.highlight();
         return get(AuthorizationMenu.class);
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AuthorizationMenu.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AuthorizationMenu.java
index 19a75cfae64..8560b6bbf09 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AuthorizationMenu.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/AuthorizationMenu.java
@@ -16,17 +16,16 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.menu;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.RoleAssignmentBrowser;
+import ch.systemsx.cisd.openbis.uitest.widget.Link;
 
 public class AuthorizationMenu extends Page
 {
 
-    @FindBy(id = "openbis_top-menu_AUTHORIZATION_MENU_ROLES")
-    private WebElement roles;
+    @Locate("openbis_top-menu_AUTHORIZATION_MENU_ROLES")
+    private Link roles;
 
     public RoleAssignmentBrowser roles()
     {
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/BrowseMenu.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/BrowseMenu.java
index 1dd63820d03..5572366e69c 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/BrowseMenu.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/BrowseMenu.java
@@ -16,25 +16,24 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.menu;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.ExperimentBrowser;
 import ch.systemsx.cisd.openbis.uitest.page.tab.ProjectBrowser;
 import ch.systemsx.cisd.openbis.uitest.page.tab.SampleBrowser;
+import ch.systemsx.cisd.openbis.uitest.widget.Link;
 
 public class BrowseMenu extends Page
 {
 
-    @FindBy(id = "openbis_top-menu_SAMPLE_MENU_BROWSE")
-    private WebElement samples;
+    @Locate("openbis_top-menu_SAMPLE_MENU_BROWSE")
+    private Link samples;
 
-    @FindBy(id = "openbis_top-menu_PROJECT_MENU_BROWSE")
-    private WebElement projects;
+    @Locate("openbis_top-menu_PROJECT_MENU_BROWSE")
+    private Link projects;
 
-    @FindBy(id = "openbis_top-menu_EXPERIMENT_MENU_BROWSE")
-    private WebElement experiments;
+    @Locate("openbis_top-menu_EXPERIMENT_MENU_BROWSE")
+    private Link experiments;
 
     public SampleBrowser samples()
     {
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/MetadataMenu.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/MetadataMenu.java
index 4ed71488f89..98ffbcdf590 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/MetadataMenu.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/MetadataMenu.java
@@ -16,29 +16,28 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.menu;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.AddPropertyType;
 import ch.systemsx.cisd.openbis.uitest.page.tab.AssignSamplePropertyType;
 import ch.systemsx.cisd.openbis.uitest.page.tab.PropertyTypeAssignmentBrowser;
 import ch.systemsx.cisd.openbis.uitest.page.tab.PropertyTypeBrowser;
+import ch.systemsx.cisd.openbis.uitest.widget.Link;
 
 public class MetadataMenu extends Page
 {
 
-    @FindBy(id = "openbis_top-menu_PROPERTY_TYPES_MENU_BROWSE_PROPERTY_TYPES")
-    private WebElement browsePropertyTypes;
+    @Locate("openbis_top-menu_PROPERTY_TYPES_MENU_BROWSE_PROPERTY_TYPES")
+    private Link browsePropertyTypes;
 
-    @FindBy(id = "openbis_top-menu_PROPERTY_TYPES_MENU_BROWSE_ASSIGNMENTS")
-    private WebElement browsePropertyTypeAssignments;
+    @Locate("openbis_top-menu_PROPERTY_TYPES_MENU_BROWSE_ASSIGNMENTS")
+    private Link browsePropertyTypeAssignments;
 
-    @FindBy(id = "openbis_top-menu_PROPERTY_TYPES_MENU_NEW_PROPERTY_TYPES")
-    private WebElement newPropertyType;
+    @Locate("openbis_top-menu_PROPERTY_TYPES_MENU_NEW_PROPERTY_TYPES")
+    private Link newPropertyType;
 
-    @FindBy(id = "openbis_top-menu_PROPERTY_TYPES_MENU_ASSIGN_TO_SAMPLE_TYPE")
-    private WebElement assignToSampleType;
+    @Locate("openbis_top-menu_PROPERTY_TYPES_MENU_ASSIGN_TO_SAMPLE_TYPE")
+    private Link assignToSampleType;
 
     public PropertyTypeBrowser propertyTypes()
     {
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/NewMenu.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/NewMenu.java
index 5b6169ea763..0aea5767eb2 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/NewMenu.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/NewMenu.java
@@ -16,25 +16,24 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.menu;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
 import ch.systemsx.cisd.openbis.uitest.page.tab.RegisterExperiment;
 import ch.systemsx.cisd.openbis.uitest.page.tab.RegisterProject;
 import ch.systemsx.cisd.openbis.uitest.page.tab.RegisterSample;
+import ch.systemsx.cisd.openbis.uitest.widget.Link;
 
 public class NewMenu extends Page
 {
 
-    @FindBy(id = "openbis_top-menu_SAMPLE_MENU_NEW")
-    private WebElement sample;
+    @Locate("openbis_top-menu_SAMPLE_MENU_NEW")
+    private Link sample;
 
-    @FindBy(id = "openbis_top-menu_PROJECT_MENU_NEW")
-    private WebElement project;
+    @Locate("openbis_top-menu_PROJECT_MENU_NEW")
+    private Link project;
 
-    @FindBy(id = "openbis_top-menu_EXPERIMENT_MENU_NEW")
-    private WebElement experiment;
+    @Locate("openbis_top-menu_EXPERIMENT_MENU_NEW")
+    private Link experiment;
 
     public RegisterSample sample()
     {
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/TypesMenu.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/TypesMenu.java
new file mode 100644
index 00000000000..49ddbb53324
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/TypesMenu.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.page.menu;
+
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
+import ch.systemsx.cisd.openbis.uitest.page.Page;
+import ch.systemsx.cisd.openbis.uitest.page.tab.ExperimentTypeBrowser;
+import ch.systemsx.cisd.openbis.uitest.page.tab.SampleTypeBrowser;
+import ch.systemsx.cisd.openbis.uitest.widget.Link;
+
+public class TypesMenu extends Page
+{
+
+    @Locate("openbis_top-menu_SAMPLE_MENU_TYPES")
+    private Link sampleTypes;
+
+    @Locate("openbis_top-menu_EXPERIMENT_MENU_TYPES")
+    private Link experimentTypes;
+
+    public SampleTypeBrowser sampleTypes()
+    {
+        sampleTypes.click();
+        return get(SampleTypeBrowser.class);
+    }
+
+    public ExperimentTypeBrowser experimentTypes()
+    {
+        experimentTypes.click();
+        return get(ExperimentTypeBrowser.class);
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/UserMenu.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/UserMenu.java
index 9276608cce8..f4a889e375c 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/UserMenu.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/menu/UserMenu.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 ETH Zuerich, CISD
+@ * Copyright 2012 ETH Zuerich, CISD
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,17 +16,16 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.menu;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.LoginPage;
 import ch.systemsx.cisd.openbis.uitest.page.Page;
+import ch.systemsx.cisd.openbis.uitest.widget.Link;
 
 public class UserMenu extends Page
 {
 
-    @FindBy(id = "openbis_top-menu_USER_MENU_LOGOUT")
-    private WebElement logout;
+    @Locate("openbis_top-menu_USER_MENU_LOGOUT")
+    private Link logout;
 
     public LoginPage logout()
     {
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AddPropertyType.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AddPropertyType.java
index 3071f0b3906..7695da66853 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AddPropertyType.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AddPropertyType.java
@@ -16,67 +16,52 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.tab;
 
-import java.util.List;
-
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.NavigationPage;
 import ch.systemsx.cisd.openbis.uitest.type.PropertyType;
 import ch.systemsx.cisd.openbis.uitest.type.PropertyTypeDataType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.DropDown;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
+import ch.systemsx.cisd.openbis.uitest.widget.TextArea;
 
 public class AddPropertyType extends NavigationPage
 {
 
-    @FindBy(id = "openbis_property-type-registration_form_code-input")
-    private WebElement code;
-
-    @FindBy(id = "openbis_property-type-registration_form_label-input")
-    private WebElement label;
+    @Locate("openbis_property-type-registration_form_code")
+    private Text code;
 
-    @FindBy(id = "openbis_property-type-registration_form_description-input")
-    private WebElement description;
+    @Locate("openbis_property-type-registration_form_label")
+    private Text label;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_data-type"),
-                @FindBy(xpath = "img") })
-    private WebElement dataTypeDropDownOpener;
-
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> dataTypeChoices;
+    @Locate("openbis_property-type-registration_form_description")
+    private TextArea description;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_vocabulary-select"),
-                @FindBy(xpath = "img") })
-    private WebElement vocabularyDropDownOpener;
+    @Locate("openbis_select_data-type")
+    private DropDown dataType;
 
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> vocabularyChoices;
+    @Locate("openbis_select_vocabulary-select")
+    private DropDown vocabulary;
 
-    @FindBy(id = "openbis_property-type-registration_formsave-button")
-    private WebElement saveButton;
+    @Locate("openbis_property-type-registration_formsave-button")
+    private Button save;
 
     public void fillWith(PropertyType propertyType)
     {
-        this.code.sendKeys(propertyType.getCode());
-        this.label.sendKeys(propertyType.getLabel());
-        this.description.sendKeys(propertyType.getDescription());
-        this.dataTypeDropDownOpener.click();
-        select(dataTypeChoices, propertyType.getDataType().getName());
+        code.write(propertyType.getCode());
+        label.write(propertyType.getLabel());
+        description.write(propertyType.getDescription());
+        dataType.select(propertyType.getDataType().getName());
 
         if (propertyType.getDataType().equals(PropertyTypeDataType.CONTROLLED_VOCABULARY))
         {
-            this.vocabularyDropDownOpener.click();
-            select(vocabularyChoices, propertyType.getVocabulary().getCode());
+            vocabulary.select(propertyType.getVocabulary().getCode());
         }
     }
 
     public AddPropertyType save()
     {
-        this.saveButton.click();
+        save.click();
         return get(AddPropertyType.class);
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AssignSamplePropertyType.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AssignSamplePropertyType.java
index 4d6fb532273..11379ab44b1 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AssignSamplePropertyType.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/AssignSamplePropertyType.java
@@ -16,72 +16,69 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.tab;
 
-import java.util.List;
-
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
 import ch.systemsx.cisd.openbis.uitest.page.NavigationPage;
 import ch.systemsx.cisd.openbis.uitest.type.PropertyTypeAssignment;
+import ch.systemsx.cisd.openbis.uitest.type.PropertyTypeDataType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Checkbox;
+import ch.systemsx.cisd.openbis.uitest.widget.DropDown;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
+import ch.systemsx.cisd.openbis.uitest.widget.Widget;
 
 public class AssignSamplePropertyType extends NavigationPage
 {
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_property-typeopenbis_property-type-assignment_SAMPLEproperty_type"),
-                @FindBy(xpath = "img") })
-    private WebElement propertyTypeDropDownOpener;
-
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> propertyTypeChoices;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_sample-typeopenbis_property-type-assignment_sample_type"),
-                @FindBy(xpath = "img") })
-    private WebElement sampleTypeDropDownOpener;
+    @Locate("openbis_select_property-typeopenbis_property-type-assignment_SAMPLEproperty_type")
+    private DropDown propertyType;
 
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> sampleTypeChoices;
+    @Locate("openbis_select_sample-typeopenbis_property-type-assignment_sample_type")
+    private DropDown sampleType;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_property-type-assignment_SAMPLEmandatory_checkbox"),
-                @FindBy(xpath = "input") })
-    private WebElement mandatoryCheckbox;
+    @Locate("openbis_property-type-assignment_SAMPLEmandatory_checkbox")
+    private Checkbox mandatory;
 
     @NotAlwaysPresent
-    @FindBys(
-        {
-                @FindBy(xpath = "//div[starts-with(@id, 'openbis_property-type-assignment_SAMPLEdefault_value')]"),
-                @FindBy(xpath = "input") })
-    private WebElement initialValue;
+    @Locate("openbis_property-type-assignment_SAMPLEdefault_value")
+    private Widget initialValue;
 
-    @FindBy(id = "openbis_property-type-assignment_SAMPLEsave-button")
-    private WebElement saveButton;
+    @Locate("openbis_property-type-assignment_SAMPLEsave-button")
+    private Button save;
 
     public void fillWith(PropertyTypeAssignment assignment)
     {
-        this.propertyTypeDropDownOpener.click();
-        select(propertyTypeChoices, assignment.getPropertyType().getLabel());
-
-        this.sampleTypeDropDownOpener.click();
-        select(sampleTypeChoices, assignment.getSampleType().getCode());
-
-        checkbox(mandatoryCheckbox, assignment.isMandatory());
+        propertyType.select(assignment.getPropertyType().getLabel());
+        sampleType.select(assignment.getSampleType().getCode());
+        mandatory.set(assignment.isMandatory());
 
-        if (assignment.getInitialValue() != null)
+        if (assignment.getInitialValue() != null && assignment.getInitialValue().length() > 0)
         {
-            this.initialValue.sendKeys(assignment.getInitialValue());
+            PropertyTypeDataType type = assignment.getPropertyType().getDataType();
+
+            if (type.equals(PropertyTypeDataType.BOOLEAN))
+            {
+                initialValue.handleAs(Checkbox.class).set(
+                        assignment.getInitialValue().equals("true"));
+            } else if (type.equals(PropertyTypeDataType.VARCHAR))
+            {
+                initialValue.handleAs(Text.class).write(assignment.getInitialValue());
+            } else if (type.equals(PropertyTypeDataType.INTEGER))
+            {
+                initialValue.handleAs(Text.class).write(assignment.getInitialValue());
+            } else if (type.equals(PropertyTypeDataType.CONTROLLED_VOCABULARY))
+            {
+                initialValue.handleAs(DropDown.class).select(assignment.getInitialValue());
+            } else
+            {
+                throw new IllegalArgumentException("Type " + type + " not supported");
+            }
         }
     }
 
     public AssignSamplePropertyType save()
     {
-        this.saveButton.click();
+        this.save.click();
         return get(AssignSamplePropertyType.class);
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentBrowser.java
index a7d06f77ec3..ee5096e6ad8 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentBrowser.java
@@ -16,54 +16,39 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.tab;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
+import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.DeletionConfirmationBox;
+import ch.systemsx.cisd.openbis.uitest.widget.TreeGrid;
 
 public class ExperimentBrowser extends BrowserPage
 {
+    @Locate("openbis_select-project")
+    private TreeGrid projectTree;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select-project"),
-                @FindBy(xpath = "..//span[not(*) and @class='gwt-InlineHTML']") })
-    private List<WebElement> spacesAndProjects;
+    @Locate("openbis_experiment-browser_delete-button")
+    private Button deleteAll;
 
-    @FindBy(id = "openbis_experiment-browser_delete-button")
-    private WebElement deleteAllButton;
+    @NotAlwaysPresent
+    @Locate("deletion-confirmation-dialog")
+    private DeletionConfirmationBox deletionDialog;
 
     public ExperimentBrowser space(String spaceCode)
     {
-        for (WebElement element : spacesAndProjects)
-        {
-            if (spaceCode.equalsIgnoreCase(element.getText()))
-            {
-                element.click();
-            }
-        }
+        projectTree.select(spaceCode);
         return this;
     }
 
     public void deleteAll()
     {
-        this.deleteAllButton.click();
-        for (WebElement ellu : this.findElements(deleteAllButton,
-                "//*[@id='deletion-confirmation-dialog']//textarea"))
-        {
-            ellu.sendKeys("reason for deletion");
-        }
-
-        List<WebElement> e = new ArrayList<WebElement>(this.findElements(deleteAllButton,
-                "//*[@id='deletion-confirmation-dialog']//button[text()='OK' or text()='Yes']"));
-        if (e.size() > 0)
-        {
-            e.get(0).click();
-        }
+        deleteAll.click();
+        deletionDialog.confirm("webdriver");
     }
 
     @Override
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentTypeBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentTypeBrowser.java
index 963c594573d..fa222a065ec 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentTypeBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ExperimentTypeBrowser.java
@@ -19,57 +19,49 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
 import ch.systemsx.cisd.openbis.uitest.page.dialog.AddExperimentTypeDialog;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class ExperimentTypeBrowser extends BrowserPage
 {
 
-    @FindBy(id = "add-entity-type-EXPERIMENT")
-    private WebElement addButton;
+    @Locate("add-entity-type-EXPERIMENT")
+    private Button add;
 
-    @FindBy(id = "edit-entity-type-EXPERIMENT")
-    private WebElement editButton;
+    @Locate("edit-entity-type-EXPERIMENT")
+    private Button edit;
 
-    @FindBy(id = "delete-entity-type-EXPERIMENT")
-    private WebElement deleteButton;
+    @Locate("delete-entity-type-EXPERIMENT")
+    private Button delete;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_experiment-type-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_experiment-type-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
+    @Locate("openbis_experiment-type-browser-grid")
+    private Grid grid;
 
     public AddExperimentTypeDialog add()
     {
-        addButton.click();
+        add.click();
         return get(AddExperimentTypeDialog.class);
     }
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return grid.getCells();
     }
 
     @Override
     protected WebElement getDeleteButton()
     {
-        return this.deleteButton;
+        return delete.getContext();
     }
 }
\ No newline at end of file
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ProjectBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ProjectBrowser.java
index 21c4c919dcf..1edf11d37d1 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ProjectBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/ProjectBrowser.java
@@ -19,44 +19,36 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class ProjectBrowser extends BrowserPage
 {
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_project-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
+    @Locate("openbis_project-browser-grid")
+    private Grid grid;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_project-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
-
-    @FindBy(id = "openbis_project-browser-delete")
-    private WebElement deleteButton;
+    @Locate("openbis_project-browser-delete")
+    private Button delete;
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return grid.getCells();
     }
 
     @Override
     protected WebElement getDeleteButton()
     {
-        return deleteButton;
+        return delete.getContext();
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeAssignmentBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeAssignmentBrowser.java
index ac5489469c4..0078ea751da 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeAssignmentBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeAssignmentBrowser.java
@@ -19,47 +19,38 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class PropertyTypeAssignmentBrowser extends BrowserPage
 {
+    @Locate("openbis_property-type-assignment-browser-grid")
+    private Grid grid;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_property-type-assignment-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
+    @Locate("openbis_property-type-assignment-browser-grid-edit")
+    private Button edit;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_property-type-assignment-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
-
-    @FindBy(id = "openbis_property-type-assignment-browser-grid-edit")
-    private WebElement editPropertyTypeButton;
-
-    @FindBy(id = "openbis_property-type-assignment-browser-grid-release")
-    private WebElement releasePropertyTypeButton;
+    @Locate("openbis_property-type-assignment-browser-grid-release")
+    private Button release;
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return grid.getCells();
     }
 
     @Override
     protected WebElement getDeleteButton()
     {
-        return this.releasePropertyTypeButton;
+        return release.getContext();
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeBrowser.java
index bd3f7333729..496169b58f1 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/PropertyTypeBrowser.java
@@ -19,47 +19,39 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class PropertyTypeBrowser extends BrowserPage
 {
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_property-type-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
+    @Locate("openbis_property-type-browser-grid")
+    private Grid grid;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_property-type-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
+    @Locate("openbis_property-type-browser-grid-add-button")
+    private Button add;
 
-    @FindBy(id = "openbis_property-type-browser-grid-add-button")
-    private WebElement addPropertyTypeButton;
-
-    @FindBy(id = "openbis_property-type-browser-grid-delete-button")
-    private WebElement deletePropertyTypeButton;
+    @Locate("openbis_property-type-browser-grid-delete-button")
+    private Button delete;
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return grid.getCells();
     }
 
     @Override
     protected WebElement getDeleteButton()
     {
-        return this.deletePropertyTypeButton;
+        return delete.getContext();
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterExperiment.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterExperiment.java
index dc6ac46e4ce..3d0ea874f8f 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterExperiment.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterExperiment.java
@@ -16,70 +16,55 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.tab;
 
-import java.util.List;
-
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
 import ch.systemsx.cisd.openbis.uitest.page.NavigationPage;
 import ch.systemsx.cisd.openbis.uitest.type.Experiment;
 import ch.systemsx.cisd.openbis.uitest.type.ExperimentType;
 import ch.systemsx.cisd.openbis.uitest.type.Sample;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.DropDown;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
+import ch.systemsx.cisd.openbis.uitest.widget.TextArea;
 
 public class RegisterExperiment extends NavigationPage
 {
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_experiment-typeopenbis_experiment-registration"),
-                @FindBy(xpath = "img") })
-    private WebElement experimentTypeList;
-
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> experimentTypeChoices;
+    @Locate("openbis_select_experiment-typeopenbis_experiment-registration")
+    private DropDown experimentTypeList;
 
     @NotAlwaysPresent
-    @FindBy(id = "openbis_generic-experiment-register_formcode-input")
-    private WebElement code;
+    @Locate("openbis_generic-experiment-register_formcode")
+    private Text code;
 
     @NotAlwaysPresent
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_projectgeneric-experiment-register_form"),
-                @FindBy(xpath = "img") })
-    private WebElement projectList;
-
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> projectChoices;
+    @Locate("openbis_select_projectgeneric-experiment-register_form")
+    private DropDown projectList;
 
     @NotAlwaysPresent
-    @FindBy(id = "generic-experiment-register_form_samples-input")
-    private WebElement samples;
+    @Locate("generic-experiment-register_form_samples")
+    private TextArea samples;
 
     @NotAlwaysPresent
-    @FindBy(id = "openbis_generic-experiment-register_formsave-button")
-    private WebElement saveButton;
+    @Locate("openbis_generic-experiment-register_formsave-button")
+    private Button saveButton;
 
     public void fillWith(Experiment experiment)
     {
-        code.sendKeys(experiment.getCode());
-        projectList.click();
-        select(projectChoices, experiment.getProject().getCode() + " ("
+        code.write(experiment.getCode());
+        projectList.select(experiment.getProject().getCode() + " ("
                 + experiment.getProject().getSpace().getCode() + ")");
 
         samples.clear();
         for (Sample sample : experiment.getSamples())
         {
-            samples.sendKeys(sample.getCode() + ", ");
+            samples.append(sample.getCode() + ", ");
         }
     }
 
     public RegisterExperiment selectExperimentType(ExperimentType experimentType)
     {
-        experimentTypeList.click();
-        select(experimentTypeChoices, experimentType.getCode());
+        experimentTypeList.select(experimentType.getCode());
         return get(RegisterExperiment.class);
     }
 
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterProject.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterProject.java
index d0f9ae8c0b7..d9d06e8e240 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterProject.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterProject.java
@@ -16,45 +16,36 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.tab;
 
-import java.util.List;
-
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
 import ch.systemsx.cisd.openbis.uitest.page.NavigationPage;
 import ch.systemsx.cisd.openbis.uitest.type.Project;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.DropDown;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
 
 public class RegisterProject extends NavigationPage
 {
 
-    @FindBy(id = "openbis_project-register_form_code-input")
-    private WebElement code;
+    @Locate("openbis_project-register_form_code")
+    private Text code;
 
     @NotAlwaysPresent
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_group-selectopenbis_project-register_form"),
-                @FindBy(xpath = "img") })
-    private WebElement spaceList;
-
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> spaceChoices;
+    @Locate("openbis_select_group-selectopenbis_project-register_form")
+    private DropDown spaces;
 
-    @FindBy(id = "openbis_project-register_formsave-button")
-    private WebElement saveButton;
+    @Locate("openbis_project-register_formsave-button")
+    private Button save;
 
     public void fillWith(Project project)
     {
-        code.sendKeys(project.getCode());
-        spaceList.click();
-        select(spaceChoices, project.getSpace().getCode());
+        code.write(project.getCode());
+        spaces.select(project.getSpace().getCode());
     }
 
     public RegisterProject save()
     {
-        this.saveButton.click();
+        save.click();
         return get(RegisterProject.class);
     }
 
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterSample.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterSample.java
index 54139db1dab..545851e1d9b 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterSample.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RegisterSample.java
@@ -18,132 +18,103 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
-import ch.systemsx.cisd.openbis.uitest.page.Fragment;
 import ch.systemsx.cisd.openbis.uitest.page.NavigationPage;
-import ch.systemsx.cisd.openbis.uitest.page.fragment.TermSelector;
-import ch.systemsx.cisd.openbis.uitest.page.fragment.VarcharInput;
 import ch.systemsx.cisd.openbis.uitest.type.PropertyType;
 import ch.systemsx.cisd.openbis.uitest.type.PropertyTypeDataType;
 import ch.systemsx.cisd.openbis.uitest.type.Sample;
 import ch.systemsx.cisd.openbis.uitest.type.SampleType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Checkbox;
+import ch.systemsx.cisd.openbis.uitest.widget.DropDown;
+import ch.systemsx.cisd.openbis.uitest.widget.Form;
+import ch.systemsx.cisd.openbis.uitest.widget.Text;
+import ch.systemsx.cisd.openbis.uitest.widget.Widget;
 
 public class RegisterSample extends NavigationPage
 {
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_sample-typeopenbis_sample-registration"),
-                @FindBy(xpath = "img") })
-    private WebElement sampleTypeList;
-
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> sampleTypeChoices;
+    @Locate("openbis_select_sample-typeopenbis_sample-registration")
+    private DropDown sampleTypes;
 
     @NotAlwaysPresent
-    @FindBy(id = "openbis_generic-sample-register_formcode-input")
-    private WebElement code;
+    @Locate("openbis_generic-sample-register_formcode")
+    private Text code;
 
     @NotAlwaysPresent
-    @FindBy(id = "openbis_generic-sample-register_formexperiment-input")
-    private WebElement experiment;
+    @Locate("openbis_generic-sample-register_formexperiment")
+    private Text experiment;
 
     @NotAlwaysPresent
-    @FindBys(
-        {
-                @FindBy(id = "register-sample-space-selection"),
-                @FindBy(xpath = "img") })
-    private WebElement spaceList;
-
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> spaceChoices;
+    @Locate("register-sample-space-selection")
+    private DropDown spaces;
 
     @NotAlwaysPresent
-    @FindBy(id = "openbis_generic-sample-register_formsave-button")
-    private WebElement saveButton;
-
-    @FindBys(
-        {
-                @FindBy(xpath = "//div[starts-with(@id, 'openbis_generic-sample-register_form')]"),
-                @FindBy(xpath = "../../label") })
-    private List<WebElement> labels;
+    @Locate("openbis_generic-sample-register_formsave-button")
+    private Button save;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_generic-sample-register_form"),
-                @FindBy(xpath = "//form") })
-    private WebElement mainForm;
+    @NotAlwaysPresent
+    @Locate("registration-panel-openbis_generic-sample-register_form")
+    private Form form;
 
     public void fillWith(Sample sample)
     {
-        code.sendKeys(sample.getCode());
-        spaceList.click();
-        select(spaceChoices, sample.getSpace().getCode());
+        code.write(sample.getCode());
+        spaces.select(sample.getSpace().getCode());
 
         Map<PropertyType, Object> properties = sample.getProperties();
 
         for (PropertyType propertyType : properties.keySet())
         {
-            Fragment propertyFragment = getFragment(propertyType);
-            propertyFragment.fillWith(properties.get(propertyType));
-        }
-    }
-
-    private Fragment getFragment(PropertyType propertyType)
-    {
-        PropertyTypeDataType type = propertyType.getDataType();
-        Fragment fragment;
-        if (type.equals(PropertyTypeDataType.CONTROLLED_VOCABULARY))
-        {
-            fragment = get(TermSelector.class);
-        } else if (type.equals(PropertyTypeDataType.VARCHAR))
-        {
-            fragment = get(VarcharInput.class);
-        } else
-        {
-            throw new IllegalArgumentException("unknown type " + type);
+            Widget w = form.getWidget(propertyType.getLabel());
+            PropertyTypeDataType type = propertyType.getDataType();
+            String value = properties.get(propertyType).toString();
+
+            switch (type)
+            {
+                case BOOLEAN:
+                    w.handleAs(Checkbox.class).fillWith(value);
+                    break;
+                case VARCHAR:
+                    w.handleAs(Text.class).fillWith(value);
+                    break;
+                case INTEGER:
+                    w.handleAs(Text.class).fillWith(value);
+                    break;
+                case CONTROLLED_VOCABULARY:
+                    w.handleAs(DropDown.class).fillWith(value);
+                    break;
+                default:
+                    throw new IllegalArgumentException(type + " not supported");
+            }
         }
-
-        fragment.setElement(getPropertyElement(propertyType));
-        return fragment;
     }
 
-    private WebElement getPropertyElement(PropertyType type)
+    public RegisterSample selectSampleType(SampleType sampleType)
     {
-        return this.findElement(this.mainForm, "//div[@id='openbis_generic-sample-register_form"
-                + type.getCode().toLowerCase().replace(".", "-DOT-") + "']");
+        sampleTypes.select(sampleType.getCode());
+        return get(RegisterSample.class);
     }
 
-    public RegisterSample selectSampleType(SampleType sampleType)
+    public RegisterSample save()
     {
-        sampleTypeList.click();
-        select(sampleTypeChoices, sampleType.getCode());
+        save.click();
         return get(RegisterSample.class);
     }
 
     public Collection<String> getProperties()
     {
         Collection<String> properties = new HashSet<String>();
-        for (WebElement label : labels)
+        for (String label : form.getLabels())
         {
-            properties.add(label.getText().replace(":", "").replace("*", "").trim());
+            properties.add(label.replace(":", "").replace("*", "").trim());
         }
         return properties;
     }
 
-    public RegisterSample save()
-    {
-        this.saveButton.click();
-        return get(RegisterSample.class);
-    }
-
     @Override
     public String toString()
     {
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RoleAssignmentBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RoleAssignmentBrowser.java
index 8f2aa5e2397..c7ff79caed2 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RoleAssignmentBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/RoleAssignmentBrowser.java
@@ -19,42 +19,27 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
 
 public class RoleAssignmentBrowser extends BrowserPage
 {
 
-    @FindBy(id = "openbis_role-browser_assign-button")
-    private WebElement assignRoleButton;
-
-    /*
-    @FindBys(
-        {
-                @FindBy(id = "openbis_sample-type-browser-grid"),
-                @FindBy(xpath = "//*[contains(@class, \"x-grid\") and contains(@class, \"-header \")]") })
-    */
-    private List<WebElement> columns;
-
-    /*
-    @FindBys(
-        {
-                @FindBy(id = "openbis_sample-type-browser-grid"),
-                @FindBy(xpath = "//*[contains(@class, \"x-grid\") and contains(@class, \"-col \")]") })
-     */
-    private List<WebElement> data;
+    @Locate("openbis_role-browser_assign-button")
+    private Button assignRoleButton;
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return null;
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return null;
     }
 
     @Override
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleBrowser.java
index cb0aecb379e..0edcab27f1a 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleBrowser.java
@@ -16,100 +16,65 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.tab;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
-import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
 import ch.systemsx.cisd.openbis.uitest.type.SampleType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.DropDown;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class SampleBrowser extends BrowserPage
 {
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_sample-browser_main-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_sample-browser_main-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
-
-    @FindBy(id = "openbis_sample-browser_main_add-button")
-    private WebElement addSampleButton;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_sample-typesample-browser-toolbar"),
-                @FindBy(xpath = "img") })
-    private WebElement sampleTypeList;
-
-    @NotAlwaysPresent
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> sampleTypeChoices;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_select_group-selectsample-browser-toolbar"),
-                @FindBy(xpath = "img") })
-    private WebElement spaceList;
-
-    @NotAlwaysPresent
-    @FindBy(className = "x-combo-list-item")
-    private List<WebElement> spaceChoices;
+    @Locate("openbis_sample-browser_main-grid")
+    private Grid grid;
+
+    @Locate("openbis_sample-browser_main_add-button")
+    private Button addSample;
+
+    @Locate("openbis_select_sample-typesample-browser-toolbar")
+    private DropDown sampleTypeList;
+
+    @Locate("openbis_select_group-selectsample-browser-toolbar")
+    private DropDown spaceList;
 
     public RegisterSample addSample()
     {
-        addSampleButton.click();
+        addSample.click();
         return get(RegisterSample.class);
     }
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return grid.getCells();
     }
 
     public SampleBrowser selectSampleType(SampleType sampleType)
     {
-        sampleTypeList.click();
-        select(sampleTypeChoices, sampleType.getCode());
+        sampleTypeList.select(sampleType.getCode());
         return get(SampleBrowser.class);
     }
 
     public SampleBrowser allSpaces()
     {
-        spaceList.click();
-        select(spaceChoices, "(all)");
-
+        spaceList.select("(all)");
         return get(SampleBrowser.class);
     }
 
     public List<String> getSampleTypes()
     {
-        List<String> sampleTypes = new ArrayList<String>();
-
-        sampleTypeList.click();
-        for (WebElement choice : sampleTypeChoices)
-        {
-            sampleTypes.add(choice.getText());
-        }
-        sampleTypeList.click();
-
-        return sampleTypes;
+        return sampleTypeList.getChoices();
     }
 
     @Override
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleTypeBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleTypeBrowser.java
index 318aa7ace27..9bb66ec118c 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleTypeBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SampleTypeBrowser.java
@@ -19,72 +19,57 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
 import ch.systemsx.cisd.openbis.uitest.page.dialog.AddSampleTypeDialog;
 import ch.systemsx.cisd.openbis.uitest.page.dialog.EditSampleTypeDialog;
 import ch.systemsx.cisd.openbis.uitest.type.SampleType;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class SampleTypeBrowser extends BrowserPage
 {
-    @FindBy(id = "add-entity-type-SAMPLE")
-    private WebElement addSampleTypeButton;
+    @Locate("add-entity-type-SAMPLE")
+    private Button add;
 
-    @FindBy(id = "edit-entity-type-SAMPLE")
-    private WebElement editSampleTypeButton;
+    @Locate("edit-entity-type-SAMPLE")
+    private Button edit;
 
-    @FindBy(id = "delete-entity-type-SAMPLE")
-    private WebElement deleteSampleTypeButton;
+    @Locate("delete-entity-type-SAMPLE")
+    private Button delete;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_sample-type-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
-
-    @FindBys(
-        {
-                @FindBy(id = "openbis_sample-type-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
+    @Locate("openbis_sample-type-browser-grid")
+    private Grid grid;
 
     public AddSampleTypeDialog add()
     {
-        addSampleTypeButton.click();
+        add.click();
         return get(AddSampleTypeDialog.class);
     }
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return grid.getCells();
     }
 
     public EditSampleTypeDialog editSampleType(SampleType type)
     {
-        for (WebElement element : data)
-        {
-            if (element.getText().equalsIgnoreCase(type.getCode()))
-            {
-                element.click();
-                editSampleTypeButton.click();
-                return get(EditSampleTypeDialog.class);
-            }
-        }
-        throw new IllegalArgumentException("Sample type browser does not contain " + type);
+        grid.select(type.getCode());
+        edit.click();
+        return get(EditSampleTypeDialog.class);
     }
 
     @Override
     protected WebElement getDeleteButton()
     {
-        return this.deleteSampleTypeButton;
+        return delete.getContext();
     }
 }
\ No newline at end of file
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SpaceBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SpaceBrowser.java
index b547c7ec39f..d159b67a178 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SpaceBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/SpaceBrowser.java
@@ -19,54 +19,46 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
 import ch.systemsx.cisd.openbis.uitest.page.dialog.AddSpaceDialog;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class SpaceBrowser extends BrowserPage
 {
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_space-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
+    @Locate("openbis_space-browser-grid")
+    private Grid grid;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_space-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
+    @Locate("openbis_space-browser_add-button")
+    private Button addSpace;
 
-    @FindBy(id = "openbis_space-browser_add-button")
-    private WebElement addSpaceButton;
-
-    @FindBy(id = "openbis_space-browser_delete-button")
-    private WebElement deleteButton;
+    @Locate("openbis_space-browser_delete-button")
+    private Button delete;
 
     public AddSpaceDialog addSpace()
     {
-        addSpaceButton.click();
+        addSpace.click();
         return get(AddSpaceDialog.class);
     }
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return this.grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return this.grid.getCells();
     }
 
     @Override
     protected WebElement getDeleteButton()
     {
-        return this.deleteButton;
+        return this.delete.getContext();
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/Trash.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/Trash.java
index 870e11cc4ef..8dac09d6bc4 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/Trash.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/Trash.java
@@ -16,23 +16,26 @@
 
 package ch.systemsx.cisd.openbis.uitest.page.tab;
 
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
+import ch.systemsx.cisd.openbis.uitest.infra.NotAlwaysPresent;
 import ch.systemsx.cisd.openbis.uitest.page.NavigationPage;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.DeletionConfirmationBox;
 
 public class Trash extends NavigationPage
 {
 
-    @FindBy(id = "empty-trash-button")
-    private WebElement empty;
+    @Locate("empty-trash-button")
+    private Button empty;
+
+    @NotAlwaysPresent
+    @Locate("deletion-confirmation-dialog")
+    private DeletionConfirmationBox deletionDialog;
 
     public Trash empty()
     {
         this.empty.click();
-        findElement(this.empty,
-                "//*[@id='deletion-confirmation-dialog']//button[text()='OK' or text()='Yes']")
-                .click();
+        deletionDialog.confirm();
 
         return get(Trash.class);
     }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/VocabularyBrowser.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/VocabularyBrowser.java
index 058e047dae5..7e35a20e155 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/VocabularyBrowser.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/tab/VocabularyBrowser.java
@@ -19,54 +19,45 @@ package ch.systemsx.cisd.openbis.uitest.page.tab;
 import java.util.List;
 
 import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.FindBys;
 
+import ch.systemsx.cisd.openbis.uitest.infra.Locate;
 import ch.systemsx.cisd.openbis.uitest.page.BrowserPage;
 import ch.systemsx.cisd.openbis.uitest.page.dialog.AddVocabularyDialog;
+import ch.systemsx.cisd.openbis.uitest.widget.Button;
+import ch.systemsx.cisd.openbis.uitest.widget.Grid;
 
 public class VocabularyBrowser extends BrowserPage
 {
+    @Locate("openbis_vocabulary-browser-grid")
+    private Grid grid;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_vocabulary-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]") })
-    private List<WebElement> columns;
+    @Locate("openbis_vocabulary-browser_add-button")
+    private Button add;
 
-    @FindBys(
-        {
-                @FindBy(id = "openbis_vocabulary-browser-grid"),
-                @FindBy(xpath = ".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]") })
-    private List<WebElement> data;
-
-    @FindBy(id = "openbis_vocabulary-browser_add-button")
-    private WebElement addVocabularyButton;
-
-    @FindBy(id = "openbis_vocabulary-browser_delete-button")
-    private WebElement deleteVocabularyButton;
+    @Locate("openbis_vocabulary-browser_delete-button")
+    private Button delete;
 
     public AddVocabularyDialog add()
     {
-        addVocabularyButton.click();
+        add.click();
         return get(AddVocabularyDialog.class);
     }
 
     @Override
     protected List<WebElement> getColumns()
     {
-        return this.columns;
+        return grid.getColumns();
     }
 
     @Override
     protected List<WebElement> getData()
     {
-        return this.data;
+        return grid.getCells();
     }
 
     @Override
     protected WebElement getDeleteButton()
     {
-        return this.deleteVocabularyButton;
+        return delete.getContext();
     }
 }
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/Fragment.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/AlertMessageBox.java
similarity index 72%
rename from ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/Fragment.java
rename to ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/AlertMessageBox.java
index e15a4e5df55..d965666bf9f 100644
--- a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/page/Fragment.java
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/AlertMessageBox.java
@@ -14,21 +14,19 @@
  * limitations under the License.
  */
 
-package ch.systemsx.cisd.openbis.uitest.page;
+package ch.systemsx.cisd.openbis.uitest.widget;
 
+import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 
 /**
  * @author anttil
  */
-public abstract class Fragment extends Page
+public class AlertMessageBox extends Widget
 {
-    protected WebElement element;
-
-    public final void setElement(WebElement element)
+    public void dismiss()
     {
-        this.element = element;
+        WebElement ok = context.findElement(By.xpath(".//button[text()=\"OK\"]"));
+        ok.click();
     }
-
-    public abstract void fillWith(Object value);
-}
\ No newline at end of file
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Button.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Button.java
new file mode 100644
index 00000000000..985e6829381
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Button.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+/**
+ * @author anttil
+ */
+public class Button extends Widget
+{
+    public void click()
+    {
+        context.click();
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Checkbox.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Checkbox.java
new file mode 100644
index 00000000000..72571fbe167
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Checkbox.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public class Checkbox extends Widget implements Fillable
+{
+    public void set(boolean value)
+    {
+        WebElement input = context.findElement(By.xpath("input"));
+        if (input.getAttribute("checked") != null ^ value)
+        {
+            input.click();
+        }
+    }
+
+    @Override
+    public void fillWith(String string)
+    {
+        set("true".equalsIgnoreCase(string));
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DeletionConfirmationBox.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DeletionConfirmationBox.java
new file mode 100644
index 00000000000..5eda8fb6eea
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DeletionConfirmationBox.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public class DeletionConfirmationBox extends Widget
+{
+    public void confirm(String reason)
+    {
+
+        WebElement text = context.findElement(By.xpath(".//textarea"));
+        text.sendKeys(reason);
+
+        WebElement ok = context.findElement(By.xpath(".//button[text()=\"OK\"]"));
+        ok.click();
+    }
+
+    public void confirm()
+    {
+        WebElement ok = context.findElement(By.xpath(".//button[text()=\"OK\"]"));
+        ok.click();
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DropDown.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DropDown.java
new file mode 100644
index 00000000000..fb245d89dc7
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/DropDown.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import ch.systemsx.cisd.openbis.uitest.infra.SeleniumTest;
+
+/**
+ * @author anttil
+ */
+public class DropDown extends Widget implements Fillable
+{
+    public void select(String text)
+    {
+        Collection<String> found = new HashSet<String>();
+        for (WebElement choice : getChoiceElements())
+        {
+            if (choice.getText().equalsIgnoreCase(text))
+            {
+                Actions builder = new Actions(SeleniumTest.driver);
+                builder.moveToElement(choice).click(choice).build().perform();
+                return;
+            }
+            found.add(choice.getText());
+        }
+        throw new IllegalArgumentException("Selection " + text + " not found, got " + found);
+    }
+
+    public List<String> getChoices()
+    {
+        List<String> choices = new ArrayList<String>();
+        for (WebElement choice : getChoiceElements())
+        {
+            choices.add(choice.getText());
+        }
+        return choices;
+    }
+
+    private List<WebElement> getChoiceElements()
+    {
+        WebElement opener = context.findElement(By.xpath(".//img"));
+        opener.click();
+        return SeleniumTest.driver.findElements(By.className("x-combo-list-item"));
+    }
+
+    @Override
+    public void fillWith(String string)
+    {
+        select(string);
+    }
+
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Fillable.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Fillable.java
new file mode 100644
index 00000000000..be2c5863796
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Fillable.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+/**
+ * @author anttil
+ */
+public interface Fillable
+{
+    public void fillWith(String string);
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Form.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Form.java
new file mode 100644
index 00000000000..46fbe6cb305
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Form.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public class Form extends Widget
+{
+    public Widget getWidget(String label)
+    {
+        List<WebElement> elements = context.findElements(By.xpath(".//form/div/label"));
+
+        for (WebElement element : elements)
+        {
+            if (element.getText().toLowerCase().startsWith(label.toLowerCase()))
+            {
+                Widget w = new Widget()
+                    {
+                    };
+                w.setContext(element.findElement(By.xpath("../div/div")));
+                return w;
+            }
+        }
+        throw new IllegalArgumentException("Could not find " + label);
+    }
+
+    public List<String> getLabels()
+    {
+        List<String> labels = new ArrayList<String>();
+        List<WebElement> elements = context.findElements(By.xpath(".//form/div/label"));
+        for (WebElement element : elements)
+        {
+            labels.add(element.getText());
+        }
+        return labels;
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Grid.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Grid.java
new file mode 100644
index 00000000000..9c66841b630
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Grid.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public class Grid extends Widget
+{
+
+    public List<WebElement> getColumns()
+    {
+        return context
+                .findElements(By
+                        .xpath(".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-header ')]//span[not(*)]"));
+    }
+
+    public List<WebElement> getCells()
+    {
+        return context
+                .findElements(By
+                        .xpath(".//td[not(ancestor::div[contains(@style,'display:none')]) and contains(@class, 'x-grid') and contains(@class, '-col ')]//*[not(*)]"));
+    }
+
+    public void select(String string)
+    {
+        Collection<String> found = new ArrayList<String>();
+        for (WebElement element : getCells())
+        {
+            if (string.equalsIgnoreCase(element.getText()))
+            {
+                element.click();
+                return;
+            }
+            found.add(element.getText());
+        }
+
+        throw new IllegalArgumentException("Grid does not contain element with text " + string
+                + ", found " + found);
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Link.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Link.java
new file mode 100644
index 00000000000..267fbb6869c
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Link.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import org.openqa.selenium.interactions.Actions;
+
+import ch.systemsx.cisd.openbis.uitest.infra.SeleniumTest;
+
+/**
+ * @author anttil
+ */
+public class Link extends Widget
+{
+    public void click()
+    {
+        context.click();
+    }
+
+    public void highlight()
+    {
+        Actions builder = new Actions(SeleniumTest.driver);
+        builder.moveToElement(context).build().perform();
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Text.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Text.java
new file mode 100644
index 00000000000..460a348ba7b
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Text.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public class Text extends Widget implements Fillable
+{
+    public void write(String text)
+    {
+        WebElement element = getInputElement();
+        element.clear();
+        element.sendKeys(text);
+    }
+
+    public void clear()
+    {
+        getInputElement().clear();
+    }
+
+    public void append(String text)
+    {
+        getInputElement().sendKeys(text);
+    }
+
+    private WebElement getInputElement()
+    {
+        if (context.getTagName().equals("input"))
+        {
+            return context;
+        } else
+        {
+            return context.findElement(By.xpath("input"));
+        }
+    }
+
+    @Override
+    public void fillWith(String string)
+    {
+        write(string);
+    }
+
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TextArea.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TextArea.java
new file mode 100644
index 00000000000..1cc461fd439
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TextArea.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public class TextArea extends Widget implements Fillable
+{
+    public void write(String text)
+    {
+        WebElement element = context.findElement(By.xpath(".//textarea"));
+        element.clear();
+        element.sendKeys(text);
+    }
+
+    public void clear()
+    {
+        WebElement element = context.findElement(By.xpath(".//textarea"));
+        element.clear();
+    }
+
+    public void append(String text)
+    {
+        WebElement element = context.findElement(By.xpath(".//textarea"));
+        element.sendKeys(text);
+    }
+
+    @Override
+    public void fillWith(String string)
+    {
+        write(string);
+    }
+
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TreeGrid.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TreeGrid.java
new file mode 100644
index 00000000000..5836f55d593
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/TreeGrid.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public class TreeGrid extends Widget
+{
+    public void select(String label)
+    {
+        List<WebElement> elements =
+                context.findElements(By.xpath(".//span[not(*) and @class='gwt-InlineHTML']"));
+
+        Collection<String> found = new ArrayList<String>();
+        for (WebElement element : elements)
+        {
+            String text = element.getText();
+            if (label.equalsIgnoreCase(text))
+            {
+                element.click();
+                return;
+            } else
+            {
+                found.add(text);
+            }
+        }
+        throw new IllegalArgumentException("Selection " + label + " not found - these were found: "
+                + found);
+    }
+}
diff --git a/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Widget.java b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Widget.java
new file mode 100644
index 00000000000..1e4816fb507
--- /dev/null
+++ b/ui-test/source/java/ch/systemsx/cisd/openbis/uitest/widget/Widget.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.uitest.widget;
+
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author anttil
+ */
+public abstract class Widget
+{
+    protected WebElement context;
+
+    public void setContext(WebElement context)
+    {
+        this.context = context;
+    }
+
+    public WebElement getContext()
+    {
+        return context;
+    }
+
+    public <T extends Widget> T handleAs(Class<T> clazz)
+    {
+        T t;
+        try
+        {
+            t = clazz.newInstance();
+        } catch (InstantiationException ex)
+        {
+            throw new RuntimeException(ex);
+        } catch (IllegalAccessException ex)
+        {
+            throw new RuntimeException(ex);
+        }
+        t.setContext(context);
+        return t;
+    }
+}
-- 
GitLab