Source code for cfme.web_ui.flash

# -*- coding: utf-8 -*-
"""Provides functions for the flash area.

:var area: A :py:class:`cfme.web_ui.Region` object representing the flash region.
"""
from functools import wraps

import cfme.fixtures.pytest_selenium as sel
from cfme.exceptions import CFMEExceptionOccured, FlashMessageException
from cfme.web_ui import Region
from cfme.utils import version
from cfme.utils.log import logger
from cfme.utils.pretty import Pretty

area = Region(
    locators={
        'message': ' | '.join([('//div[starts-with(@id, "flash_") and '
                'not(ancestor::*[contains(@style,"display: none")])]'
                '//div[contains(@class,"alert")]'), '//div[@id="flash_div"]', ])  # login screen
    }
)

_mapping_new = {
    "alert-warning": "warning",
    "alert-success": "success",
    "alert-danger": "error",
    "alert-info": "info"
}


[docs]class Message(Pretty): """ A simple class to represent a flash error in CFME. Args: message: The message string. level: The level of the message. """ pretty_attrs = ['message', 'level'] def __init__(self, message=None, level=None): self.message = message self.level = level
@version.dependent def get_message_level(el): return sel.get_attribute(el, "class") or "error" @get_message_level.method('5.3')
[docs]def get_message_level_up(el): _class = sel.get_attribute(el, "class") for key, value in _mapping_new.iteritems(): if key in _class: return value return "error"
@version.dependent def get_message_text(el): strong = sel.elements("./strong", root=el) if strong: return sel.text(strong[0]) else: return sel.text(el) @get_message_text.method('5.3')
[docs]def get_message_text_up(el): return sel.text(el)
[docs]def message(el): """ Turns an element into a :py:class:`Message` object. Args: el: The element containing the flass message. Returns: A :py:class:`Message` object. """ return Message(message=get_message_text(el), level=get_message_level(el)) # no class attr on login screen
[docs]def get_messages(): """Return a list of visible flash messages""" sel.wait_for_ajax() return map(message, [e for e in sel.elements(area.message) if sel.is_displayed(e)])
[docs]def dismiss(): """Dismiss the current flash message""" element = sel.element(area.message) if version.current_version() < '5.7': sel.click(element) else: close_button = sel.element('.//button[@class="close"]', root=element) sel.click(close_button)
[docs]def get_all_messages(): """Returns a list of all flash messages, (including ones hidden behind the currently showing one, if any). All flash messages will be dismissed.""" all_messages = [] while sel.is_displayed(area.message): all_messages = all_messages + get_messages() dismiss() return all_messages
[docs]def is_error(message): """ Checks a given message to see if is an Error.' Args: message: The message object. """ return message.level in ('error',)
[docs]def verify_rails_error(f): # Wrapper that checks the rails error before the flash message @wraps(f) def g(*args, **kwargs): sel.wait_for_ajax() # Just in case error = sel.get_rails_error() if error is not None: raise CFMEExceptionOccured( "Flash message check failed because of following rails error:\n{}".format(error)) return f(*args, **kwargs) return g
[docs]def verpick_message(f): """Wrapper that resolves eventual verpick dictionary passed to the function.""" # TODO: If we find such use for more places, this would deserve extraction in utils/ @wraps(f) def g(m, *args, **kwargs): if isinstance(m, dict): m = version.pick(m) return f(m, *args, **kwargs) return g
[docs]def onexception_printall(f): """If FlashMessageException happens, appends all the present flash messages in the error text""" @wraps(f) def g(*args, **kwargs): try: return f(*args, **kwargs) except FlashMessageException as e: err_text = str(e) messages = get_messages() if not messages: raise # Just reraise the original messages = ['{}: {}'.format(message.level, message.message) for message in messages] new_err_text = '{}\nPresent flash messages:\n{}'.format(err_text, '\n'.join(messages)) raise type(e)(new_err_text) return g
@verify_rails_error
[docs]def assert_no_errors(messages=None): """Asserts that there are no current Error messages. If no messages are passed in, they will be retrieved from the UI.""" all_messages = messages or get_messages() errors = [error.message for error in filter(is_error, all_messages)] if errors: raise FlashMessageException(', '.join(errors)) else: return all_messages
@verify_rails_error
[docs]def assert_success(messages=None): """Asserts that there is 1 or more successful messages, no errors. If no messages are passed in, they will be retrieved from the UI.""" all_messages = messages or get_messages() errors = [error.message for error in filter(is_error, all_messages)] if not all_messages: raise FlashMessageException('No flash messages found') elif errors: raise FlashMessageException(', '.join(errors)) else: return all_messages
@verify_rails_error @verpick_message @onexception_printall
[docs]def assert_message_match(m): """ Asserts that a message matches a specific string.""" logger.debug('Asserting flash message match for %s', m) if not any([fm.message == m for fm in get_messages()]): logger.debug(' No match found in...%s', get_messages()) raise FlashMessageException("No matching flash message for '{}'".format(m))
@verify_rails_error @verpick_message @onexception_printall
[docs]def assert_message_contain(m): """ Asserts that a message contains a specific string """ if not any([m in fm.message for fm in get_messages()]): raise FlashMessageException("No flash message contains '{}'".format(m))
@verify_rails_error @verpick_message @onexception_printall
[docs]def assert_success_message(m): """Asserts that there are no errors and a (green) info message matches the given string.""" messages = get_messages() logger.info('Asserting flash success message against messages: {}'.format(messages)) assert_no_errors(messages) if not any([ (fm.message == m and (fm.level in {"info", "success"})) for fm in messages]): raise FlashMessageException( "No matching info flash message for '{}', instead got {}".format(m, messages))