Esempio n. 1
0
def menu_option(menu_label, option_label, browser=default_browser,
                query_info=None):
    """Returns the link node (``<a>``) of an option in a menu.

    :param menu_label: The label of the menu.
    :type menu_label: string
    :param menu_label: The label of the option to find in the menu.
    :type menu_label: string
    :param browser: The browser instance to operate with. Uses the global singleton
      default browser by default.
    :type browser: :py:class:`ftw.testbrowser.core.Browser`
    :returns: The option link node.
    :rtype: :py:class:`ftw.testbrowser.nodes.NodeWrapper`
    :raises: :py:exc:`ftw.testbrowser.exceptions.NoElementFound`
    """

    menu_container = menu(menu_label, browser=browser, query_info=query_info)
    option_label = normalize_spaces(option_label)
    for link in menu_container.css(
            # Plone 4
            '.actionMenuContent a, '
            # Plone 5
            '> ul > li > a'):
        if normalize_spaces(link.text_content()) == option_label:
            return link

    query_info.add_hint('Options in menu {!r}: {!r}'.format(
        menu_label,
        menu_options(menu_label, browser=browser)))
    raise NoElementFound(query_info)
Esempio n. 2
0
def menu(label, browser=default_browser, query_info=None):
    """Finds a menu by label and returns its ``<dl class="actionMenu">`` node.

    :param label: The label of the menu to find.
    :type label: string
    :param browser: The browser instance to operate with. Uses the global singleton
      default browser by default.
    :type browser: :py:class:`ftw.testbrowser.core.Browser`
    :returns: The menu container node.
    :rtype: :py:class:`ftw.testbrowser.nodes.NodeWrapper`
    :raises: :py:exc:`ftw.testbrowser.exceptions.NoElementFound`
    """

    label = normalize_spaces(label).rstrip(u'\u2026')

    if IS_PLONE_4:
        menus_node = container(browser=browser).css('#contentActionMenus').first
        for span in menus_node.css('.actionMenuHeader > a > span:first-child'):
            if normalize_spaces(span.text_content()).rstrip(u'\u2026') == label:
                return span.parent('.actionMenu')

    else:
        for menu in container(browser=browser).css('nav li[id^="plone-contentmenu-"]'):
            for span in menu.css('a > span.plone-toolbar-title'):
                if normalize_spaces(span.text_content()).rstrip(u'\u2026') == label:
                    return menu

    query_info.add_hint('Visible menus: {!r}.'.format(menus(browser=browser)))
    raise NoElementFound(query_info)
Esempio n. 3
0
    def find_field(self, label_or_name):
        """Finds and returns a field by label or name.

        :param label_or_name: The label or the name of the field.
        :type label_or_name: string
        :returns: The field node
        :rtype: :py:class:`ftw.testbrowser.nodes.NodeWrapper`
        """
        label = normalize_spaces(label_or_name)

        for input in self.inputs:
            if input.name == label_or_name:
                return input

            if input.label is None:
                continue

            if label in (input.label.text,
                         normalize_spaces(input.label.raw_text)):
                return input

            checkbox_labels = (input.label is not None
                               and input.label.css('>span.label'))
            if checkbox_labels and label == checkbox_labels.first.text:
                return input

        return self.find_widget(label_or_name)
Esempio n. 4
0
    def fill(self, values):
        """Fill the widget inputs with the values passed as arguments.

        :param values: a list of names and / or labels of the options
        :type values: list of string
        """
        if not isinstance(values, (list, tuple, set)):
            values = [values]

        # deselect existing options
        for input in self.inputs:
            input.checked = False

        # normalize value labels to names
        reverse_option_map = dict(map(reversed, self.option_map.items()))
        values = [reverse_option_map.get(value, value) for value in values]

        # fill new values
        for input in self.inputs:
            if 'value' not in input.attrib:
                continue

            if input.attrib['value'] in values:
                input.checked = True
                values.remove(input.attrib['value'])

        if values:
            available_options = self.options
            raise OptionsNotFound(normalize_spaces(self.label.raw_text),
                                  values, available_options)
Esempio n. 5
0
    def find_widget(self, label):
        """Finds a Plone widget (div.field) in a form.

        :param label: The label of the widget.
        :type label: string
        :returns: Returns the field node or `None`.
        :rtype: :py:class:`ftw.testbrowser.nodes.NodeWrapper`
        """

        label = normalize_spaces(label)

        label_node_xpath = '//label[normalize-space(text())="%s"]' % label
        div_node_xpath = '//div[contains(concat(" ",' + \
            'normalize-space(@class)," ")," label ")]' + \
            '[normalize-space(text())="%s"]' % label
        label_xpath = ' | '.join((label_node_xpath, div_node_xpath))

        for label_node in self.xpath(label_xpath):
            if not label_node.within(self):
                continue

            field = label_node.parent(css='div.field')
            if field:
                return field

        return None
Esempio n. 6
0
    def field_labels(self):
        """A list of label texts and field names of each field in this form.

        The list contains the whitespace normalized label text of
        each field.
        If there is no label or it has an empty text, the fieldname
        is used instead.

        :returns: A list of label texts (and field names).
        :rtype: list of strings
        """
        labels = []
        for input in self.inputs:
            label = (input.label is not None
                     and normalize_spaces(input.label.text))
            if label:
                labels.append(label)
            elif input.name:
                labels.append(input.name)

            checkbox_labels = (input.label is not None
                               and input.label.css('>span.label'))
            if checkbox_labels:
                labels.append(checkbox_labels.first.text)

        return labels
Esempio n. 7
0
def document_description(browser=default_browser):
    """Returns the whitespace-normalized document description of the
    current page or None.
    """
    nodes = browser.css('.documentDescription')
    if len(nodes) == 0:
        return None
    return normalize_spaces(nodes.first.text_content())
Esempio n. 8
0
    def normalized_outerHTML(self):
        """The whitespace-normalized HTML of the current node and its children.
        The HTML-Tag of the current node is included.
        All series of whitespaces (including non-breaking spaces) are replaced
        with a single space.

        :returns: HTML
        :rtype: unicode
        """
        return normalize_spaces(self.outerHTML)
Esempio n. 9
0
    def normalized_innerHTML(self):
        """The whitespace-normalized HTML content of the current node.
        The HTML-Tag of the current node is not included.
        All series of whitespaces (including non-breaking spaces) are replaced
        with a single space.

        :returns: HTML
        :rtype: unicode
        """
        return normalize_spaces(self.innerHTML)
Esempio n. 10
0
    def normalized_text(self, recursive=True):
        """Returns the whitespace-normalized text of the current node.
        This includes the text of each node within this node recurively.
        All whitespaces are reduced to a single space each.

        .. deprecated:: 1.3.1
           Use property :py:func:`ftw.testbrowser.nodes.NodeWrapper.text`
           instead.

        :param recursive: Set to ``False`` for not including text of
            contained tags.
        :type recursive: Boolean (default: ``True``)
        :returns: The whitespace normalized text content.
        :rtype: unicode
        """
        if recursive:
            return normalize_spaces(self.text_content())
        else:
            return normalize_spaces(self.raw_text or '')
Esempio n. 11
0
    def find_link_by_text(self, text, within=None):
        """Searches for a link with the passed text.
        The comparison is done with normalized whitespace and includes the full
        text within the link, including its subelements' texts.

        :param text: The text to be looked for.
        :type text: string
        :param within: A node object for limiting the scope of the search.
        :type within: :py:class:`ftw.testbrowser.nodes.NodeWrapper`.
        :returns: The link object or `None` if nothing matches.
        :rtype: :py:class:`ftw.testbrowser.nodes.LinkNode`
        """

        text = normalize_spaces(text)
        if within is None:
            within = self

        for link in within.css('a'):
            if normalize_spaces(link.text_content()) == text:
                return link

        return None
Esempio n. 12
0
    def find(self, text):
        """Find a cell of this table by text.
        When nothing is found, it falls back to the default ``find`` behavior.

        .. seealso:: :py:func:`ftw.testbrowser.nodes.NodeWrapper.find`

        :param text: The text to be looked for.
        :type text: string
        :returns: A single node object or `None` if nothing matches.
        :rtype: :py:class:`ftw.testbrowser.nodes.NodeWrapper`
        """
        text = normalize_spaces(text)
        for cell in self.cells:
            if cell.normalized_text() == text:
                return cell

        return super(Table, self).find(text)
Esempio n. 13
0
def messages(browser=default_browser):
    """Returns a dict with lists of status messages (normalized text) for
    "info", "warning" and "error".
    """

    messages = {'info': [],
                'warning': [],
                'error': []}

    for message in browser.css('.portalMessage'):
        type_classes = (set(message.classes) & set(messages.keys()))
        if not type_classes:
            # unkown message type - skip it
            continue

        key = tuple(type_classes)[0]

        if message.css('dd'):
            # Plone 4: <dl class="portalMessage info">
            #              <dt>Info</dt>
            #              <dd>Message</dd>
            #          </dl>
            text = normalize_spaces(' '.join(message.css('dd').text_content()))

        elif message.css('strong'):
            # Plone 5: <div class="portalMessage info">
            #              <strong>Info</strong>
            #              Message
            #          </div>
            type_text = message.css('strong').first.text
            text = re.sub(r'^{} *'.format(re.escape(type_text)), '', message.text)

        if not text:
            # message is empty - skip it
            continue

        messages[key].append(text)

    return messages
Esempio n. 14
0
def erroneous_fields(form):
    """Returns a mapping of erroneous fields (key is label or name of
    the field) to a list of error messages for the fields on the form
    passed as argument.

    :param form: The form node to check for errors.
    :type form: :py:class:`ftw.testbrowser.form.Form`
    :returns: A dict of erroneous fields with error messages.
    :rtype: dict
    """

    result = {}
    for input in form.inputs:
        if not input.parent('.field.error'):
            continue

        if input.label is not None:
            label = input.label.text_content()
        if not label:
            label = input.name

        errors = input.parent('.field').css('.fieldErrorBox').normalized_text()
        result[normalize_spaces(label)] = errors
    return result
Esempio n. 15
0
def messages(browser=default_browser):
    """Returns a dict with lists of status messages (normalized text) for
    "info", "warning" and "error".
    """

    messages = {'info': [],
                'warning': [],
                'error': []}

    for message in browser.css('.portalMessage'):
        type_classes = (set(message.classes) & set(messages.keys()))
        if not type_classes:
            # unkown message type - skip it
            continue

        key = tuple(type_classes)[0]
        text = normalize_spaces(' '.join(message.css('dd').text_content()))
        if not text:
            # message is empty - skip it
            continue

        messages[key].append(text)

    return messages
Esempio n. 16
0
def first_heading(browser=default_browser):
    """Returns the whitespace-normalized first heading of the current page.
    """
    first_heading = browser.css('.documentFirstHeading').first
    return normalize_spaces(first_heading.text_content())