Exemple #1
0
    def create(self, name, provider, public_key=None, cancel=False):
        """Create new keyPair.

        Args:
            name (str): name of the KeyPair
            public_key (str): RSA Key if present
            provider (str): Cloud Provider
            cancel (boolean): Cancel Keypair creation
        """

        view = navigate_to(self, 'Add')
        changed = view.form.fill({
            'name': name,
            'public_key': public_key,
            'provider': provider.name
        })
        if cancel and not changed:
            view.form.cancel.click()
            flash_message = 'Add of new Key Pair was cancelled by the user'
        else:
            view.form.add.click()
            flash_message = VersionPick({
                Version.lowest():
                'Creating Key Pair {}'.format(name),
                '5.8':
                'Key Pair "{}" created'.format(name)
            }).pick(self.appliance.version)

        # add/cancel should redirect, new view
        view = self.create_view(KeyPairAllView)
        # TODO BZ 1444520 causing ridiculous redirection times after submitting the form
        wait_for(lambda: view.is_displayed,
                 num_sec=240,
                 delay=2,
                 fail_func=view.flush_widget_cache,
                 handle_exception=True)
        assert view.is_displayed
        view.entities.flash.assert_success_message(flash_message)
        keypair = self.instantiate(name, provider, public_key=public_key)
        return keypair
class ProvidersManagePoliciesView(BaseLoggedInPage):
    """
     Provider's Manage Policies view
    """
    policies = VersionPick({
        Version.lowest(): DynaTree('protect_treebox'),
        '5.7': BootstrapTreeview('protectbox')
    })

    @View.nested
    class entities(BaseNonInteractiveEntitiesView):  # noqa
        @property
        def entity_class(self):
            return ProviderEntity().pick(self.browser.product_version)

    save = Button('Save')
    reset = Button('Reset')
    cancel = Button('Cancel')

    @property
    def is_displayed(self):
        return False
def pytest_generate_tests(metafunc):
    """
    Build a list of tuples containing (group_name, context)
    Returns:
        tuple containing (group_name, context)
        where group_name is a string and context is ViaUI/SSUI
    """
    appliance = find_appliance(metafunc)
    parameter_list = []
    id_list = []
    # TODO: Include SSUI role_access dict and VIASSUI context
    role_access_ui = VersionPick({
        Version.lowest(): role_access_ui_58z,
        '5.9': role_access_ui_59z,
        '5.10': role_access_ui_510z
    }).pick(appliance.version)
    logger.info('Using the role access dict: %s', role_access_ui)
    roles_and_context = [(role_access_ui, ViaUI)]
    for role_access, context in roles_and_context:
        for group in role_access.keys():
            parameter_list.append((group, role_access, context))
            id_list.append('{}-{}'.format(group, context))
    metafunc.parametrize('group_name, role_access, context', parameter_list)
Exemple #4
0
class AlertProfilesEditAssignmentsView(ControlExplorerView):
    title = Text("#explorer_title_text")
    assign_to = BootstrapSelect("chosen_assign_to")
    tag_category = BootstrapSelect("chosen_cat")
    selections = CbTree(
        VersionPick({
            Version.lowest(): "obj_treebox",
            "5.9": "object_treebox"
        }))
    header = Text("//div[@id='alert_profile_assign_div']/h3")
    based_on = Text('//label[normalize-space(.)="Based On"]/../div')

    save_button = Button("Save")
    reset_button = Button("Reset")
    cancel_button = Button("Cancel")

    @property
    def is_displayed(self):
        return (self.in_control_explorer
                and self.title.text == 'Alert Profile "{}"'.format(
                    self.context["object"].description)
                and self.header.text == "Assignments"
                and self.based_on == self.context["object"].TYPE)
class EditGroupSequenceView(ConfigurationView):
    """ Edit Groups Sequence View in CFME UI """

    group_order_selector = VersionPick({
        Version.lowest(): UpDownSelect(
            '#seq_fields',
            './/a[@title="Move selected fields up"]/img',
            './/a[@title="Move selected fields down"]/img'),
        '5.8': UpDownSelect(
            '#seq_fields',
            '//button[@title="Move selected fields up"]/i',
            '//button[@title="Move selected fields down"]/i'), })

    save_button = Button('Save')
    reset_button = Button('Reset')
    cancel_button = Button('Cancel')

    @property
    def is_displayed(self):
        return (
            self.accordions.accesscontrol.is_opened and
            self.title.text == "Editing Sequence of User Groups"
        )
Exemple #6
0
    def create(self, cancel=False):
        """Create new keypair"""
        def refresh():
            self.provider.refresh_provider_relationships()
            view.browser.selenium.refresh()
            view.flush_widget_cache()

        view = navigate_to(self, 'Add')
        view.form.fill({
            'name': self.name,
            'public_key': self.public_key,
            'provider': self.provider.name
        })
        if cancel:
            view.form.cancel.click()
            flash_message = 'Add of new Key Pair was cancelled by the user'
        else:
            view.form.add.click()
            flash_message = VersionPick({
                Version.lowest():
                'Creating Key Pair {}'.format(self.name),
                '5.8':
                'Key Pair "{}" created'.format(self.name)
            }).pick(self.appliance.version)

        # add/cancel should redirect, new view
        view = self.create_view(KeyPairAllView)
        # TODO BZ 1444520 causing ridiculous redirection times after submitting the form
        wait_for(lambda: view.is_displayed,
                 fail_condition=False,
                 num_sec=120,
                 delay=3,
                 fail_func=lambda: view.flush_widget_cache(),
                 handle_exception=True)
        view = self.create_view(KeyPairAllView)
        view.entities.flash.assert_no_error()
        view.entities.flash.assert_success_message(flash_message)
Exemple #7
0
class MyServicesView(BaseLoggedInPage):
    def in_myservices(self):
        return (self.logged_in_as_current_user
                and self.navigation.currently_selected
                == ['Services', 'MyServices'])

    @property
    def is_displayed(self):
        return (self.in_myservices and self.configuration.is_displayed
                and not self.myservice.is_dimmed)

    @View.nested
    class myservice(Accordion):  # noqa
        ACCORDION_NAME = "Services"

        tree = ManageIQTree()

    @View.nested
    class toolbar(View):  # noqa
        """
         represents provider toolbar and its controls
        """
        configuration = Dropdown(text='Configuration')
        policy = Dropdown(text='Policy')
        lifecycle = Dropdown(text='Lifecycle')
        download = Dropdown(text='Download')
        view_selector = View.nested(ItemsToolBarViewSelector)

    # TODO drop '_btn' suffix
    reload = Button(title=VersionPick({
        Version.lowest(): 'Reload current display',
        '5.9': 'Refresh this page'
    }))
    configuration = Dropdown('Configuration')
    policy_btn = Dropdown('Policy')
    lifecycle_btn = Dropdown('Lifecycle')
    download_choice = Dropdown('Download')
Exemple #8
0
    def delete(self, cancel=False, wait=False):
        view = navigate_to(self, 'Details')
        # TODO: get rid of this resolve when widgetastic.core/pull/68 is merged
        item_name = VersionPick({Version.lowest(): 'Remove this Key Pair',
                                '5.9': 'Remove this Key Pair from Inventory'}
                                ).pick(self.appliance.version)
        view.toolbar.configuration.item_select(item_name,
                                               handle_alert=(not cancel))
        # cancel doesn't redirect, confirmation does
        view.flush_widget_cache()
        if cancel:
            view = self.create_view(KeyPairDetailsView)
        else:
            view = self.create_view(KeyPairAllView)
        wait_for(lambda: view.is_displayed, fail_condition=False, num_sec=10, delay=1)

        # flash message only displayed if it was deleted
        if not cancel:
            view.flash.assert_no_error()
            view.flash.assert_success_message('Delete initiated for 1 Key Pair')

        if wait:
            def refresh():
                self.provider.refresh_provider_relationships()
                view.browser.refresh()
                view.flush_widget_cache()

            view = navigate_to(self.parent, 'All')

            wait_for(
                lambda: self.name in view.entities.all_entity_names,
                message="Wait keypairs to disappear",
                fail_condition=True,
                num_sec=300,
                delay=5,
                fail_func=refresh
            )
class ContainerAllView(ContainerObjectAllBaseView):
    """Containers All view"""
    summary = Text(VersionPick({
        Version.lowest(): '//h3[normalize-space(.) = {}]'.format(quote('All Containers')),
        '5.8': '//h1[normalize-space(.) = {}]'.format(quote('Containers'))
    }))
    containers = Table(locator="//div[@id='list_grid']//table")

    @View.nested
    class Filters(Accordion):  # noqa
        ACCORDION_NAME = "Filters"

        @View.nested
        class Navigation(VerticalNavigation):
            DIV_LINKS_MATCHING = './/div/ul/li/a[contains(text(), {txt})]'

            def __init__(self, parent, logger=None):
                VerticalNavigation.__init__(self, parent, '#Container_def_searches', logger=logger)

        tree = ManageIQTree()

    @property
    def is_displayed(self):
        return self.summary.is_displayed
Exemple #10
0
 class retirement(Tab):  # noqa
     # TODO Somehow need to handle a modal window
     copy_from_provisioning = Button("Copy from provisioning")
     repository = BootstrapSelect("retirement_repository_id")
     playbook = BootstrapSelect("retirement_playbook_id")
     machine_credential = BootstrapSelect(
         "retirement_machine_credential_id")
     cloud_type = BootstrapSelect("retirement_cloud_type")
     cloud_credential = BootstrapSelect("retirement_cloud_credential_id")
     localhost = Input(id="retirement_inventory_localhost")
     specify_host_values = Input(id="retirement_inventory_specify")
     hosts = Input("retirement_inventory")
     logging_output = BootstrapSelect("retirement_log_output")
     max_ttl = Input("retirement_execution_ttl")
     escalate_privilege = BootstrapSwitch("retirement_become_enabled")
     verbosity = BootstrapSelect("retirement_verbosity")
     remove_resources = VersionPick({
         Version.lowest():
         BootstrapSelect("vm.catalogItemModel.retirement_remove_resources"),
         "5.9":
         BootstrapSelect(
             "vm.vm.catalogItemModel.retirement_remove_resources")
     })
     extra_vars = AnsibleExtraVariables(tab="retirement")
Exemple #11
0
class ImageRegistryDetailsView(ContainerObjectDetailsBaseView):
    """Container Image Registries Detail view"""
    SUMMARY_TEXT = VersionPick({
        Version.lowest(): 'Image Registries',
        '5.9': 'Container Image Registries'
    })
Exemple #12
0
class ImageRegistryAllView(ContainerObjectAllBaseView):
    """Container Images Registries All view"""
    SUMMARY_TEXT = VersionPick({
        Version.lowest(): 'Image Registries',
        '5.9': 'Container Image Registries'
    })
Exemple #13
0
class PodAllView(ContainerObjectAllBaseView):
    """Container Pods All view"""
    SUMMARY_TEXT = VersionPick({
        Version.lowest(): 'Pods',
        '5.9': 'Container Pods'
    })
Exemple #14
0
class RouteAllView(ContainerObjectAllBaseView):
    """Container Routes All view"""
    SUMMARY_TEXT = VersionPick({
        Version.lowest(): 'Routes',
        '5.9': 'Container Routes'
    })
Exemple #15
0
class OrderForm(ServicesCatalogView):
    """Represents the order form of a service.
    This form doesn't have a static set of elements apart from titles and buttons. In the most cases
    the fields can be either regular inputs or dropdowns. Their locators depend on field names. In
    order to find and fill required fields a parametrized view is used here. The keys of a fill
    dictionary should match ids of the fields. For instance there is a field with such html
    <input id="some_key"></input>, so a fill dictionary should look like that:
    {"some_key": "some_value"}
    """
    title = Text('#explorer_title_text')
    dialog_title = Text(
        VersionPick({
            Version.lowest(): ".//div[@id='main_div']//h3",
            "5.9": ".//div[@id='main_div']//h2"
        })
    )

    @ParametrizedView.nested
    class fields(ParametrizedView):  # noqa
        PARAMETERS = ("key",)
        input = Input(id=Parameter("key"))
        select = Select(id=Parameter("key"))
        param_input = Input(id=ParametrizedString("param_{key}"))
        dropdown = VersionPick({
            Version.lowest(): BootstrapSelect(Parameter("key")),
            "5.9": BootstrapSelect(locator=ParametrizedString(
                ".//div[contains(@class, 'bootstrap-select') and "
                "select[@id={key|quote}]]"))
        })
        param_dropdown = VersionPick({
            Version.lowest(): BootstrapSelect(ParametrizedString("param_{key}")),
            "5.9": BootstrapSelect(locator=ParametrizedString(
                ".//div[contains(@class, 'bootstrap-select') and "
                "select[@id='param_{key}']]"))
        })

        @property
        def visible_widget(self):
            if self.input.is_displayed:
                return self.input
            elif self.dropdown.is_displayed:
                return self.dropdown
            elif self.param_input.is_displayed:
                return self.param_input
            elif self.param_dropdown.is_displayed:
                return self.param_dropdown
            elif self.select.is_displayed:
                return self.select
            else:
                raise ItemNotFound("Visible widget is not found")

        def read(self):
            return self.visible_widget.read()

        def fill(self, value):
            return self.visible_widget.fill(value)

    def fill(self, fill_data):
        values = deflatten_dict(fill_data)
        was_change = False
        self.before_fill(values)
        for key, value in values.items():
            widget = self.fields(key)
            if value is None:
                self.logger.debug('Skipping fill of %r because value was None', key)
                continue
            try:
                if widget.fill(value):
                    was_change = True
            except NotImplementedError:
                continue

        self.after_fill(was_change)
        return was_change
Exemple #16
0
 def step(self):
     self.prerequisite_view.toolbar.configuration.item_select(
         VersionPick({
             Version.lowest(): 'Add Existing Containers Provider',
             '5.9': 'Add a new Containers Provider'
         }).pick(self.obj.appliance.version))
Exemple #17
0
class ProjectDetailsView(ContainerObjectDetailsBaseView):
    """Container Projects Detail view"""
    SUMMARY_TEXT = VersionPick({
        Version.lowest(): 'Projects',
        '5.9': 'Container Projects'
    })
Exemple #18
0
 def step(self, *args, **kwargs):
     navigation_path = VersionPick(self.obj.navigation_path).pick(
         self.obj.appliance.version)
     self.prerequisite_view.navigation.select(*navigation_path)
Exemple #19
0
 def in_manager(self):
     navigation_path = VersionPick(
         self.context['object'].navigation_path).pick(
             self.context['object'].appliance.version)
     return (self.logged_in_as_current_user
             and self.navigation.currently_selected == navigation_path)
 def view_classes(self):
     return VersionPick({
         Version.lowest(): RetirementView,
         "5.9": RetirementViewWithOffset
     })
Exemple #21
0
 def container_provider_edit_view_class(self):
     return VersionPick({
         Version.lowest(): ContainerProviderEditView,
         '5.9': ContainerProviderEditViewUpdated
     })
Exemple #22
0
 class mytasks(Tab):  # noqa
     TAB_NAME = VersionPick({
         Version.lowest(): 'My VM and Container Analysis Tasks',
         '5.9': 'My Tasks'
     })
     table = Table(table_loc)
Exemple #23
0
 class TestForm(View):
     table = Table('#withwidgets', column_widgets={
         'Column 2': TextInput(locator='./input'),
         'Column 3': VersionPick({
             Version.lowest(): TextInput(locator='./input'),
             '2.0': None})})
def DeploymentRoleEntity():  # noqa
    """Temporary wrapper for Deployment Role Entity during transition to JS based Entity """
    return VersionPick({
        Version.lowest(): NonJSDepRoleEntity,
        '5.9': JSBaseEntity,
    })
Exemple #25
0
class LoginPage(View):
    flash = FlashMessages(
        VersionPick({
            Version.lowest(): 'div#flash_text_div',
            '5.8': '//div[@class="flash_text_div"]'
        })
    )

    class details(View):  # noqa
        region = Text('.//p[normalize-space(text())="Region:"]/span')
        zone = Text('.//p[normalize-space(text())="Zone:"]/span')
        appliance = Text('.//p[normalize-space(text())="Appliance:"]/span')

    change_password = Text('.//a[normalize-space(.)="Update password"]')
    back = Text('.//a[normalize-space(.)="Back"]')
    username = Input(name='user_name')
    password = Input(name='user_password')
    new_password = Input(name='user_new_password')
    verify_password = Input(name='user_verify_password')
    login = Button(id='login')

    def show_update_password(self):
        if not self.new_password.is_displayed:
            self.change_password.click()

    def hide_update_password(self):
        if self.new_password.is_displayed:
            self.back.click()

    def login_admin(self, **kwargs):
        username = conf.credentials['default']['username']
        password = conf.credentials['default']['password']
        cred = Credential(principal=username, secret=password)
        from cfme.configure.access_control import User
        user = User(credential=cred, name='Administrator')
        return self.log_in(user, **kwargs)

    def submit_login(self, method='click_on_login'):
        if method == 'click_on_login':
            self.login.click()
        elif method == 'press_enter_after_password':
            self.browser.send_keys(Keys.ENTER, self.password)
        elif method == '_js_auth_fn':
            self.browser.execute_script('miqAjaxAuth();')
        else:
            raise ValueError('Unknown method {}'.format(method))
        if self.flash.is_displayed:
            self.flash.assert_no_error()

    def log_in(self, user, method='click_on_login'):
        self.fill({
            'username': user.credential.principal,
            'password': user.credential.secret,
        })
        self.submit_login(method)
        logged_in_view = self.browser.create_view(BaseLoggedInPage)
        if logged_in_view.logged_in:
            if user.name is None:
                name = logged_in_view.current_fullname
                self.logger.info(
                    'setting the appliance.user.name to %r because it was not specified', name)
                user.name = name
            self.extra.appliance.user = user

    def update_password(
            self, username, password, new_password, verify_password=None,
            method='click_on_login'):
        self.show_update_password()
        self.fill({
            'username': username,
            'password': password,
            'new_password': new_password,
            'verify_password': verify_password if verify_password is not None else new_password
        })
        self.submit_login(method)

    def logged_in_as_user(self, user):
        return False

    @property
    def logged_in_as_current_user(self):
        return False

    @property
    def current_username(self):
        return None

    @property
    def current_fullname(self):
        return None

    @property
    def logged_in(self):
        return not self.logged_out

    @property
    def logged_out(self):
        return self.username.is_displayed and self.password.is_displayed and self.login.is_displayed

    @property
    def is_displayed(self):
        return self.logged_out
Exemple #26
0
class ButtonGroup(Updateable, Navigatable):
    """Create,Edit and Delete Button Groups

    Args:
        text: The button Group name.
        hover: The button group hover text.
        type: The object type.
    """
    CLUSTER = "Cluster"
    DATASTORE = "Datastore"
    HOST = VersionPick({version.LOWEST: "Host", '5.4': "Host / Node"})
    PROVIDER = "Provider"
    SERVICE = "Service"
    TEMPLATE = "VM Template and Image"
    VM_INSTANCE = "VM and Instance"

    def __init__(self, text=None, hover=None, type=None, appliance=None):
        Navigatable.__init__(self, appliance=appliance)
        self.text = text
        self.hover = hover
        self.type = type

    def create(self):
        view = navigate_to(self, 'Add')
        view.fill({
            'text': self.text,
            'hover': self.hover,
            'image': 'Button Image 1',
        })
        view.add_button.click()
        view = self.create_view(ButtonGroupObjectTypeView)
        assert view.is_displayed
        view.flash.assert_no_error()
        view.flash.assert_message('Buttons Group "{}" was added'.format(
            self.hover))

    def update(self, updates):
        view = navigate_to(self, 'Edit')
        changed = view.fill(updates)
        if changed:
            view.save_button.click()
        else:
            view.cancel_button.click()
        view = self.create_view(ButtonGroupDetailView, override=updates)
        assert view.is_displayed
        view.flash.assert_no_error()
        if changed:
            view.flash.assert_message('Buttons Group "{}" was saved'.format(
                updates.get('hover', self.hover)))
        else:
            view.flash.assert_message(
                'Edit of Buttons Group "{}" was cancelled by the user'.format(
                    self.text))

    def delete(self, cancel=False):
        view = navigate_to(self, 'Details')
        view.configuration.item_select('Remove this Button Group',
                                       handle_alert=not cancel)
        if cancel:
            assert view.is_displayed
            view.flash.assert_no_error()
        else:
            view = self.create_view(ButtonGroupObjectTypeView)
            assert view.is_displayed
            view.flash.assert_no_error()
            view.flash.assert_message(
                'Buttons Group "{}": Delete successful'.format(self.hover))

    @property
    def exists(self):
        try:
            navigate_to(self, 'Details')
            return True
        except CandidateNotFound:
            return False

    def delete_if_exists(self):
        if self.exists:
            self.delete()
Exemple #27
0
class RouteDetailsView(ContainerObjectDetailsBaseView):
    """Container Routes Detail view"""
    SUMMARY_TEXT = VersionPick({
        Version.lowest(): 'Routes',
        '5.9': 'Container Routes'
    })
Exemple #28
0
 class myothertasks(Tab):  # noqa
     TAB_NAME = VersionPick({
         '5.9': 'My Tasks',
         Version.lowest(): 'My Other UI Tasks'
     })
     table = Table(table_loc)
Exemple #29
0
def DatastoreEntity():  # noqa
    """Temporary wrapper for Datastore Entity during transition to JS based Entity """
    return VersionPick({
        Version.lowest(): NonJSDatastoreEntity,
        '5.9': JSDatastoreEntity,
    })
Exemple #30
0
class ExpressionEditor(View, Pretty):
    """This class enables to embed the expression in a Form.

    Args:
        show_func: Function to call to show the expression if there are more of them.
    """
    @View.nested
    class field_form_view(View):  # noqa
        type = BootstrapSelect("chosen_typ")
        field = BootstrapSelect("chosen_field")
        key = BootstrapSelect("chosen_key")
        value = Input(name="chosen_value")
        user_input = Input(name="user_input")

    @View.nested
    class field_date_form(View):  # noqa
        dropdown_select = BootstrapSelect("chosen_from_1")
        input_select_date = Calendar(name="miq_date_1_0")
        input_select_time = BootstrapSelect("miq_time_1_0")

    @View.nested
    class count_form_view(View):  # noqa
        type = BootstrapSelect("chosen_typ")
        count = BootstrapSelect("chosen_count")
        key = BootstrapSelect("chosen_key")
        value = Input(name="chosen_value")
        user_input = Input(name="user_input")

    @View.nested
    class tag_form_view(View):  # noqa
        type = BootstrapSelect("chosen_typ")
        tag = BootstrapSelect("chosen_tag")
        value = BootstrapSelect("chosen_value")
        user_input = Input(name="user_input")

    @View.nested
    class find_form_view(View):  # noqa
        type = BootstrapSelect("chosen_typ")
        field = BootstrapSelect("chosen_field")
        skey = BootstrapSelect("chosen_skey")
        value = Input(name="chosen_value")
        check = BootstrapSelect("chosen_check")
        cfield = BootstrapSelect("chosen_cfield")
        ckey = BootstrapSelect("chosen_ckey")
        cvalue = Input(name="chosen_cvalue")

    @View.nested
    class registry_form_view(View):  # noqa
        type = BootstrapSelect("chosen_typ")
        key = Input(name="chosen_regkey")
        value = Input(name="chosen_regval")
        operation = BootstrapSelect("chosen_key")
        contents = Input(name="chosen_value")

    @View.nested
    class date_specific_form_view(View):  # noqa
        date = Calendar(name="miq_date_1_0")
        time = BootstrapSelect("miq_time_1_0")

    @View.nested
    class date_relative_form_view(View):  # noqa
        from_ = BootstrapSelect("chosen_from_1")
        through = BootstrapSelect("chosen_through_1")

    ROOT = "//div[@id='exp_editor_div']"
    MAKE_BUTTON = "//span[not(contains(@style,'none'))]//img[@alt='{}']"
    ATOM_ROOT = "./div[@id='exp_atom_editor_div']"
    EXPRESSIONS_ROOT = "./fieldset/div"
    COMMIT = VersionPick({
        Version.lowest():
        "//img[@alt='Commit expression element changes']",
        "5.7.1":
        Button(title="Commit expression element changes"),
    })
    DISCARD = VersionPick({
        Version.lowest():
        "//img[@alt='Discard expression element changes']",
        "5.7.1":
        Button(title="Discard expression element changes"),
    })
    REMOVE = VersionPick({
        Version.lowest(): ("//span[not(contains(@style, 'none'))]/"
                           "/img[@alt='Remove this expression element']"),
        "5.8":
        Button(title="Remove this expression element"),
    })
    NOT = VersionPick({
        Version.lowest():
        ("//span[not(contains(@style, 'none'))]"
         "//img[@alt='Wrap this expression element with a NOT']"),
        "5.8":
        Button(title="Wrap this expression element with a NOT"),
    })
    OR = VersionPick({
        Version.lowest(): ("//span[not(contains(@style, 'none'))]/"
                           "/img[@alt='OR with a new expression element']"),
        "5.8":
        Button(title="OR with a new expression element"),
    })
    AND = VersionPick({
        Version.lowest(): ("//span[not(contains(@style, 'none'))]/"
                           "/img[@alt='AND with a new expression element']"),
        "5.8":
        Button(title="AND with a new expression element"),
    })
    REDO = VersionPick({
        Version.lowest(): "//img[@alt='Redo']",
        "5.8": Button(title="Redo the last change"),
    })
    UNDO = VersionPick({
        Version.lowest(): "//img[@alt='Undo']",
        "5.8": Button(title="Undo the last change"),
    })
    SELECT_SPECIFIC = "//img[@alt='Click to change to a specific Date/Time format']"
    SELECT_RELATIVE = "//img[@alt='Click to change to a relative Date/Time format']"

    pretty_attrs = ['show_loc']

    def __init__(self, parent, show_loc=None, logger=None):
        View.__init__(self, parent, logger=logger)
        self.show_loc = show_loc

    def __locator__(self):
        return self.ROOT

    def click_undo(self):
        self.browser.click(self.UNDO)

    def click_redo(self):
        self.browser.click(self.REDO)

    def click_and(self):
        self.browser.click(self.AND)

    def click_or(self):
        self.browser.click(self.OR)

    def click_not(self):
        self.browser.click(self.NOT)

    def click_remove(self):
        self.browser.click(self.REMOVE)

    def click_commit(self):
        self.browser.click(self.COMMIT)

    def click_discard(self):
        self.browser.click(self.DISCARD)

    def click_switch_to_relative(self):
        self.browser.click(self.SELECT_RELATIVE)

    def click_switch_to_specific(self):
        self.browser.click(self.SELECT_SPECIFIC)

    @property
    def _atom_root(self):
        return self.browser.element(self.ATOM_ROOT)

    @property
    def _expressions_root(self):
        return self.browser.element(self.EXPRESSIONS_ROOT)

    def select_first_expression(self):
        """There is always at least one (???), so no checking of bounds."""
        self.browser.elements("//a[contains(@id,'exp_')]",
                              parent=self._expressions_root)[0].click()

    def select_expression_by_text(self, text):
        self.browser.click(
            "//a[contains(@id,'exp_')][contains(normalize-space(text()),'{}')]"
            .format(text))

    def no_expression_present(self):
        els = self.browser.elements("//a[contains(@id,'exp_')]",
                                    parent=self._expressions_root)
        if len(els) > 1:
            return False
        return els[0].text.strip() == "???"

    def any_expression_present(self):
        return not self.no_expression_present()

    def is_editing(self):
        try:
            self.browser.element(
                "//a[contains(@id,'exp_')][contains(normalize-space(text()),'???')]",
                parent=self._expressions_root)
            return True
        except NoSuchElementException:
            return False

    def delete_whole_expression(self):
        while self.any_expression_present():
            self.select_first_expression()
            self.click_remove()

    def read(self):
        """Returns whole expression as represented visually."""
        return self._expressions_root.text.encode("utf-8").strip()

    def enable_editor(self):
        try:
            el = self.browser.element(self.show_loc)
            wait_for(lambda: el.is_displayed, num_sec=2, delay=0.2)
            el.click()
        except (TimedOutError, NoSuchElementException):
            pass

    def fill(self, expression):
        if self.show_loc is not None:
            self.enable_editor()
        prog = create_program(expression, self)
        before = self._expressions_root.text.encode("utf-8").strip()
        prog()
        after = self._expressions_root.text.encode("utf-8").strip()
        return before != after

    def fill_count(self, count=None, key=None, value=None):
        """ Fills the 'Count of' type of form.

        If the value is unspecified and we are in the advanced search form (user input),
        the user_input checkbox will be checked if the value is None.

        Args:
            count: Name of the field to compare (Host.VMs, ...).
            key: Operation to do (=, <, >=, ...).
            value: Value to check against.
        Returns: See :py:func:`cfme.web_ui.fill`.
        """
        view = self.count_form_view
        view.fill(dict(type="Count of", count=count, key=key, value=value))
        # In case of advanced search box
        if view.user_input.is_displayed:
            user_input = value is None
            view.user_input.fill(user_input)
        self.click_commit()

    def fill_tag(self, tag=None, value=None):
        """ Fills the 'Tag' type of form.

        Args:
            tag: Name of the field to compare.
            value: Value to check against.
        """
        view = self.tag_form_view
        view.fill(dict(type="Tag", tag=tag, value=value))
        # In case of advanced search box
        if view.user_input.is_displayed:
            user_input = value is None
            view.user_input.fill(user_input)
        self.click_commit()

    def fill_registry(self,
                      key=None,
                      value=None,
                      operation=None,
                      contents=None):
        """ Fills the 'Registry' type of form."""
        view = self.registry_form_view
        view.fill(
            dict(
                type="Registry",
                key=key,
                value=value,
                operation=operation,
                contents=contents,
            ))
        self.click_commit()

    def fill_find(self,
                  field=None,
                  skey=None,
                  value=None,
                  check=None,
                  cfield=None,
                  ckey=None,
                  cvalue=None):
        view = self.find_form_view
        view.fill(
            dict(type="Find",
                 field=field,
                 skey=skey,
                 value=value,
                 check=check,
                 cfield=cfield,
                 ckey=ckey,
                 cvalue=cvalue))
        self.click_commit()

    def fill_field(self, field=None, key=None, value=None):
        """ Fills the 'Field' type of form.

        Args:
            tag: Name of the field to compare (Host.VMs, ...).
            key: Operation to do (=, <, >=, IS NULL, ...).
            value: Value to check against.
        Returns: See :py:func:`cfme.web_ui.fill`.
        """
        field_norm = field.strip().lower()
        if ("date updated" in field_norm or "date created" in field_norm
                or "boot time" in field_norm or "timestamp" in field_norm):
            no_date = False
        else:
            no_date = True
        view = self.field_form_view
        view.fill(
            dict(
                type="Field",
                field=field,
                key=key,
                value=value if no_date else None,
            ))
        # In case of advanced search box
        if view.user_input.is_displayed:
            user_input = value is None
            view.user_input.fill(user_input)
        if not no_date:
            # Flip the right part of form
            view = self.field_date_form
            if (isinstance(value, basestring)
                    and not re.match(r"^[0-9]{2}/[0-9]{2}/[0-9]{4}$", value)):
                if not view.dropdown_select.is_displayed:
                    self.click_switch_to_relative()
                view.fill({"dropdown_select": value})
                self.click_commit()
            else:
                # Specific selection
                if not view.input_select_date.is_displayed:
                    self.click_switch_to_specific()
                if (isinstance(value, tuple)
                        or isinstance(value, list)) and len(value) == 2:
                    date, time = value
                elif isinstance(value,
                                basestring):  # is in correct format mm/dd/yyyy
                    # Date only (for now)
                    date = value[:]
                    time = None
                else:
                    raise TypeError(
                        "fill_field expects a 2-tuple (date, time) or string with date"
                    )
                # TODO datetime.datetime support
                view.input_select_date.fill(date)
                # Try waiting a little bit for time field
                # If we don't wait, committing the expression will glitch
                try:
                    wait_for(lambda: view.input_select_time.is_displayed,
                             num_sec=6)
                    # It appeared, so if the time is to be set, we will set it
                    # (passing None glitches)
                    if time:
                        view.input_select_time.fill(time)
                except TimedOutError:
                    # Did not appear, ignore that
                    pass
                finally:
                    # And finally, commit the expression :)
                    self.click_commit()
        else:
            self.click_commit()