Esempio n. 1
0
class ObjectiveDialog(DialogPage):
    """ Dialog for adding a new objective. """

    expr = InputElement((By.ID, 'objective-expr'))
    name = InputElement((By.ID, 'objective-name'))
    ok = ButtonElement((By.ID, 'objective-ok'))
    cancel = ButtonElement((By.ID, 'objective-cancel'))
Esempio n. 2
0
class LoginPage(BasePageObject):
    """ There doesn't seem to be a 'login' page anymore... """

#    url = '/accounts/login/?next=/'
    url = '/'

    username = InputElement((By.ID, 'id_username'))
    password = InputElement((By.ID, 'id_password'))
    submit_button = ButtonElement((By.XPATH,
                                   '/html/body/div/div[2]/form/input'))

    def login_successfully(self, username, password):
        """ Login using valid parameters. """
        self.username = username
        self.password = password
        self.submit()
        from project import ProjectsListPage
        return ProjectsListPage(self.browser, self.port)

    def login_unsuccessfully(self, username, password):
        """ Login using invalid parameters. """
        self.username = username
        self.password = password
        self.submit()
        return LoginPage(self.browser, self.port)

    def magic_login(self, username, password):
        '''Need a way to login to the app directly,
        not manually via the GUI'''
        pass
    
    def submit(self):
        """ Clicks the login button. """
        self('submit_button').click()
Esempio n. 3
0
class MetadataModal(BootstrapModal):

    submit_button = None
    cancel_button = None

    project_name = InputElement((By.ID, 'id_project_name'))
    description = InputElement((By.ID, 'id_description'))
    version = InputElement((By.ID, 'id_version'))

    def cancel(self):
        """ Clicks the 'cancel' button """
        self('cancel_button').click()

    def submit(self):
        """ Clicks the 'confirm' button """
        self('submit_button').click()

    def submit_metadata(self, project_name, description=None, version=None):
        """ Submits metadata for a project. """
        self.project_name = project_name
        if description is not None:
            self.description = description
        if version is not None:
            self.version = version
        self.submit()
Esempio n. 4
0
class ProjectPage(BasePageObject):
    """ Superclass of :class:`NewProjectPage` and :class:`ProjectInfoPage`. """

    title_prefix = 'Project:'

    project_name = InputElement((By.ID, 'id_projectname'))
    description = InputElement((By.ID, 'id_description'))
    version = InputElement((By.ID, 'id_version'))
Esempio n. 5
0
class ConstraintDialog(DialogPage):
    """ Dialog for adding a new constraint. """

    expr = InputElement((By.ID, 'constraint-expr'))
    scaler = InputElement((By.ID, 'constraint-scaler'))
    adder = InputElement((By.ID, 'constraint-adder'))
    name = InputElement((By.ID, 'constraint-name'))
    ok = ButtonElement((By.ID, 'constraint-ok'))
    cancel = ButtonElement((By.ID, 'constraint-cancel'))
Esempio n. 6
0
class ImplicitComponentPage(ComponentPage):
    """ Implicit Component editor page. """

    states_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='States']"))
    residuals_tab = ButtonElement(
        (By.XPATH, "div/ul/li/a[text()='Residuals']"))

    states = GridElement((By.ID, 'States_props'))
    residuals = GridElement((By.ID, 'Residuals_props'))

    states_filter = InputElement((By.ID, 'States_variableFilter'))
    states_clear = ButtonElement((By.ID, 'States_clear'))

    residuals_filter = InputElement((By.ID, 'Residuals_variableFilter'))
    residuals_clear = ButtonElement((By.ID, 'Residuals_clear'))

    def set_state(self, name, value):
        """ Set state `name` to `value`. """
        self('states_tab').click()
        grid = self.states
        found = []
        for row in grid.rows:
            if row[1] == name:
                row[2] = value
                return
            found.append(row[1])
        raise RuntimeError('%r not found in states %s' % (name, found))

    def get_states(self, return_type=None):
        """ Return states grid. """
        self('states_tab').click()
        return self._get_variables(self.states, return_type)

    def get_residuals(self, return_type=None):
        """ Return residuals grid. """
        self('residuals_tab').click()
        return self._get_variables(self.residuals, return_type)

    def get_state(self, name, return_type=None):
        """ Return first state variable with `name`. """
        self('states_tab').click()
        return self._get_variable(name, self.states, return_type)

    def get_residual(self, name, return_type=None):
        """ Return first residual variable with `name`. """
        self('residuals_tab').click()
        return self._get_variable(name, self.residuals, return_type)

    def show_states(self):
        """switch to states tab"""
        self('states_tab').click()

    def show_residuals(self):
        """switch to residuals tab"""
        self('residuals_tab').click()
Esempio n. 7
0
class ImportDialog(MetadataModal):
    """ Dialog for importing a project """

    submit_button = ButtonElement(
        (By.XPATH,
         "form/div[@class='modal-footer']/button[text()='Import Project']"))
    cancel_button = ButtonElement(
        (By.XPATH, "form/div[@class='modal-footer']/button[text()='Cancel']"))
    load_button = ButtonElement(
        (By.XPATH,
         "form/div[@class='modal-footer']/button[text()='Load Project']"))

    ##### need to add input element for file input
    #####      self.input_element = "" # path to project file
    projectfile_input = InputElement((By.ID, 'id_projectfile'))

    def load_project(self, projectfile_path):
        '''Just load the project using the dialog. This
           does not complete the import. That is a separate step
        '''
        self.projectfile_input = projectfile_path
        self('load_button').click()

    @staticmethod
    def get_random_project_name(size=6, chars=None):
        """ Return a random project name. """
        chars = chars or string.ascii_uppercase + string.digits
        return "testing project " + \
               ''.join(random.choice(chars) for x in range(size))
class ConverterPage(BasePage):
    """Page class for currency converter page, extends BasePage class"""

    # adding InputElement object to the ConverterPage
    input_element = InputElement(ConverterPageLocators.INPUT_FIELD)

    # adding two currency_lists to the ConverterPage:
    # one for initial currency and one for desirable currency
    initial_currency_element = CurrencyElement(
        ConverterPageLocators.FROM_CURRENCY_FIELD,
        ConverterPageLocators.FROM_CURRENCY_LIST_ELEMENT,
        ConverterPageLocators.CONVERTER_FROM_SELECT)
    desirable_currency_element = CurrencyElement(
        ConverterPageLocators.TO_CURRENCY_FIELD,
        ConverterPageLocators.TO_CURRENCY_LIST_ELEMENT,
        ConverterPageLocators.CONVERTER_TO_SELECT)

    def is_title_matches(self):
        """Verifies that we are on the converter's page"""
        return "Калькулятор иностранных валют" in self.driver.title

    def click_submit_button(self):
        """Triggers the exchange"""
        element = self.driver.find_element(
            *ConverterPageLocators.SUBMIT_BUTTON)
        element.click()
        WebDriverWait(self.driver,
                      10).until(lambda driver: driver.find_element(
                          *ConverterPageLocators.RESULT_VALUE).text != '')

    def choose_cash_to_cash_exchange(self):
        """Choosing cash-to-cash exchange type
        by clicking corresponding checkboxes
        """
        self.driver.find_element(*ConverterPageLocators.FROM_TYPE_CASH).click()
        self.driver.find_element(*ConverterPageLocators.TO_TYPE_CASH).click()

    def get_current_exchange_rates(self):
        """Grabbing current buy and sell exchange rates
        and return them as tuple
        """
        buy_exchange_rate = self.driver.find_element(
            *ConverterPageLocators.BUY_EXCHANGE_RATE).text.replace(',', '.')
        sell_exchange_rate = self.driver.find_element(
            *ConverterPageLocators.SELL_EXCHANGE_RATE).text.replace(',', '.')
        return (buy_exchange_rate, sell_exchange_rate)

    def get_final_currency(self):
        """Return final currency that we get after exchange"""
        return self.driver.find_element(
            *ConverterPageLocators.RESULT_CURRENCY).text

    def get_final_result(self):
        """Return formatted money amount after suggested exchange"""
        return self.driver.find_element(*ConverterPageLocators.RESULT_VALUE).\
            text.replace(' ', '').replace(',', '.')
Esempio n. 9
0
class ParameterDialog(DialogPage):
    """ Dialog for adding a new parameter. """

    target = InputElement((By.ID, 'parameter-target'))
    low = InputElement((By.ID, 'parameter-low'))
    high = InputElement((By.ID, 'parameter-high'))
    scaler = InputElement((By.ID, 'parameter-scaler'))
    adder = InputElement((By.ID, 'parameter-adder'))
    name = InputElement((By.ID, 'parameter-name'))
    ok = ButtonElement((By.ID, 'parameter-ok'))
    cancel = ButtonElement((By.ID, 'parameter-cancel'))
Esempio n. 10
0
class ValuePrompt(BasePageObject):
    """ Overlay displayed by ``openmdao.Util.promptForValue()``. """

    prompt = TextElement((By.ID, 'get-value-prompt'))
    value = InputElement((By.ID, 'get-value-input'))
    ok_button = ButtonElement((By.ID, 'get-value-ok'))
    cancel_button = ButtonElement((By.ID, 'get-value-cancel'))

    def set_value(self, value):
        self.value = value + Keys.RETURN

    def set_text(self, text):
        self.value = text

    def click_ok(self):
        self('ok_button').click()

    def click_cancel(self):
        self('cancel_button').click()
Esempio n. 11
0
class ParameterDialog(DialogPage):
    """ Dialog for adding a new parameter. """

    target = InputElement((By.ID, 'parameter-target'))
    low = InputElement((By.ID, 'parameter-low'))
    high = InputElement((By.ID, 'parameter-high'))
    scaler = InputElement((By.ID, 'parameter-scaler'))
    adder = InputElement((By.ID, 'parameter-adder'))
    name = InputElement((By.ID, 'parameter-name'))
    ok = ButtonElement((By.ID, 'parameter-ok'))
    cancel = ButtonElement((By.ID, 'parameter-cancel'))

    def get_autocomplete_targets(self, target):
        self.target = target
        return self.browser.find_elements_by_xpath("//ul[contains(@class, 'ui-autocomplete')]/li")
Esempio n. 12
0
class UploadPage(BasePageObject):
    """ Pops-up when adding a file. """

    title_prefix = 'OpenMDAO: Add File'

    filename = InputElement((By.NAME, 'myfile'))
    submit = ButtonElement((By.ID, 'add-button'))

    def upload_file(self, path):
        self.filename = path
        self('submit').click()

    def upload_files(self):
        self('submit').click()

    def select_file(self, path):
        self.filename = path

    def select_files(self, paths):
        for path in paths:
            self.select_file(path)
Esempio n. 13
0
class ArgsPrompt(BasePageObject):
    """ Dialog displayed by ``openmdao.Util.promptForArgs()``. """

    prompt = TextElement((By.ID, 'get-args-prompt'))
    name = InputElement((By.ID, 'get-args-name'))
    ok_button = ButtonElement((By.ID, 'get-args-ok'))
    cancel_button = ButtonElement((By.ID, 'get-args-cancel'))

    def set_name(self, value):
        self.name = value + Keys.RETURN

    def set_text(self, text):
        self.name = text

    def set_argument(self, index, text):
        table = self.browser.find_element(By.ID, 'get-args-tbl')
        arg_inputs = table.find_elements(By.XPATH, 'tbody/tr/td/input')
        arg_inputs[index].send_keys(text)

    def argument_count(self):
        self.browser.implicitly_wait(1)
        try:
            table = self.browser.find_elements_by_css_selector('#get-args-tbl')
        finally:
            self.browser.implicitly_wait(TMO)
        if table:
            arg_inputs = table[0].find_elements(By.XPATH, 'tbody/tr/td/input')
            return len(arg_inputs)
        else:
            return 0

    def click_ok(self):
        self('ok_button').click()

    def click_cancel(self):
        self('cancel_button').click()
Esempio n. 14
0
class ArgsPrompt(BasePageObject):
    """ Dialog displayed by ``openmdao.Util.promptForArgs()``. """

    prompt = TextElement((By.ID, 'get-args-prompt'))
    name = InputElement((By.ID, 'get-args-name'))
    ok_button = ButtonElement((By.ID, 'get-args-ok'))
    cancel_button = ButtonElement((By.ID, 'get-args-cancel'))

    def set_name(self, value):
        self.name = value + Keys.RETURN

    def set_text(self, text):
        self.name = text

    def set_argument(self, index, text):
        table = self.browser.find_element(By.ID, 'get-args-tbl')
        arg_inputs = table.find_elements(By.XPATH, 'tbody/tr/td/input')
        arg_inputs[index].send_keys(text)

    def click_ok(self):
        self('ok_button').click()

    def click_cancel(self):
        self('cancel_button').click()
Esempio n. 15
0
 def search_box(self):
     locator = SearchPageLocater.search_box
     return InputElement(driver=self.driver, locator=locator)
Esempio n. 16
0
class EventDialog(DialogPage):
    """ Dialog for adding a new event. """

    target = InputElement((By.ID, 'event-target'))
    ok = ButtonElement((By.ID, 'event-ok'))
    cancel = ButtonElement((By.ID, 'event-cancel'))
Esempio n. 17
0
class WorkspacePage(BasePageObject):

    title_prefix = 'OpenMDAO:'

    # Top.
    project_menu = ButtonElement((By.ID, 'project-menu'))
    commit_button = ButtonElement((By.ID, 'project-commit'))
    revert_button = ButtonElement((By.ID, 'project-revert'))
    reload_button = ButtonElement((By.ID, 'project-reload'))
    close_button = ButtonElement((By.ID, 'project-close'))
    exit_button = ButtonElement((By.ID, 'project-exit'))

    view_menu = ButtonElement((By.ID, 'view-menu'))
    objects_button = ButtonElement((By.ID, 'view-components'))
    console_button = ButtonElement((By.ID, 'view-console'))
    dataflow_button = ButtonElement((By.ID, 'view-dataflow'))
    files_button = ButtonElement((By.ID, 'view-files'))
    library_button = ButtonElement((By.ID, 'view-library'))
    properties_button = ButtonElement((By.ID, 'view-properties'))
    workflow_button = ButtonElement((By.ID, 'view-workflow'))
    refresh_button = ButtonElement((By.ID, 'view-refresh'))

    tools_menu = ButtonElement((By.ID, 'tools-menu'))
    editor_button = ButtonElement((By.ID, 'tools-editor'))
    plotter_button = ButtonElement((By.ID, 'tools-plotter'))
    drawing_button = ButtonElement((By.ID, 'tools-drawing'))
    log_button = ButtonElement((By.ID, 'tools-log'))

    help_menu = ButtonElement((By.ID, 'help-menu'))
    doc_button = ButtonElement((By.ID, 'help-doc'))

    about_button = ButtonElement((By.ID, 'about-item'))

    # Left side.
    objects_tab = ButtonElement((By.ID, 'otree_tab'))
    objects_selector = SelectElement((By.ID, 'otree_pane_select'))
    files_tab = ButtonElement((By.ID, 'ftree_tab'))

    # Object context menu.
    obj_properties = ButtonElement((By.XPATH, "//a[(@rel='properties')]"))
    obj_dataflow = ButtonElement((By.XPATH, "//a[(@rel='show_dataflow')]"))
    obj_workflow = ButtonElement((By.XPATH, "//a[(@rel='show_workflow')]"))
    obj_run = ButtonElement((By.XPATH, "//a[(@rel='run')]"))
    #obj_toggle     = ButtonElement((By.XPATH, "//a[(@rel='toggle')]"))
    obj_remove = ButtonElement((By.XPATH, "//a[(@rel='remove')]"))

    # File menu
    file_menu = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/a'))
    newfile_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/ul/li[1]/a'))
    newfolder_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/ul/li[2]/a'))
    add_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/ul/li[3]/a'))
    delete_files_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/ul/li[4]/a'))

    # File tree pane context menu
    ftree_newfile_button = ButtonElement(
        (By.XPATH, "//ul[@id='ftree_pane-context-menu']/li[1]]"))
    ftree_newfolder_button = ButtonElement(
        (By.XPATH, "//ul[@id='ftree_pane-context-menu']/li[2]"))
    ftree_add_button = ButtonElement(
        (By.XPATH, "//ul[@id='ftree_pane-context-menu']/li[3]"))
    ftree_toggle_files_button = ButtonElement(
        (By.XPATH, "//ul[@id='ftree_pane-context-menu']/li[4]"))

    # File context menu.
    file_create = ButtonElement((By.XPATH, "//a[(@rel='createFile')]"))
    file_add = ButtonElement((By.XPATH, "//a[(@rel='addFile')]"))
    file_folder = ButtonElement((By.XPATH, "//a[(@rel='createFolder')]"))
    file_view = ButtonElement((By.XPATH, "//a[(@rel='viewFile')]"))
    file_edit = ButtonElement((By.XPATH, "//a[(@rel='editFile')]"))
    file_exec = ButtonElement((By.XPATH, "//a[(@rel='execFile')]"))
    file_image = ButtonElement((By.XPATH, "//a[(@rel='viewImage')]"))
    file_geometry = ButtonElement((By.XPATH, "//a[(@rel='viewGeometry')]"))
    file_rename = ButtonElement((By.XPATH, "//a[(@rel='renameFile')]"))
    file_delete = ButtonElement((By.XPATH, "//a[(@rel='deleteFile')]"))
    file_toggle = ButtonElement((By.XPATH, "//a[(@rel='toggle')]"))

    file_chooser = InputElement((By.ID, 'filechooser'))

    # Center.
    dataflow_tab = ButtonElement((By.ID, 'dataflow_tab'))
    workflow_tab = ButtonElement((By.ID, 'workflow_tab'))

    # Right side.
    properties_tab = ButtonElement((By.ID, 'properties_tab'))
    props_header = TextElement((By.XPATH, "//div[@id='properties_pane']/h3"))
    props_inputs = GridElement(
        (By.XPATH, "//div[@id='properties_pane']/div[1]"))
    props_outputs = GridElement(
        (By.XPATH, "//div[@id='properties_pane']/div[2]"))

    library_tab = ButtonElement((By.ID, 'library_tab'))
    library_search = InputElement((By.ID, 'objtt-filter'))
    library_clear = ButtonElement((By.ID, 'objtt-clear'))

    library_item_docs = ButtonElement(
        (By.XPATH, "//ul[@id='lib-cmenu']/li[1]"))
    library_item_metadata = ButtonElement(
        (By.XPATH, "//ul[@id='lib-cmenu']/li[2]"))

    # Bottom.
    history = TextElement((By.ID, 'history'))
    command = InputElement((By.ID, 'cmdline'))
    submit = ButtonElement((By.ID, 'command-button'))

    def __init__(self, browser, port):
        super(WorkspacePage, self).__init__(browser, port)

        self.locators = {}
        self.locators["objects"] = \
            (By.XPATH, "//div[@id='otree_pane']//li[@path]")
        self.locators["files"] = \
            (By.XPATH, "//div[@id='ftree_pane']//a[@class='file ui-draggable']")

        # Wait for bulk of page to load.
        WebDriverWait(
            self.browser,
            TMO).until(lambda browser: len(self.get_dataflow_figures()) > 0)

        # Now wait for all WebSockets open.
        browser.execute_script('openmdao.project.webSocketsReady(2);')

        try:  # We may get 2 notifiers: sockets open and sockets closed.
            NotifierPage.wait(self, base_id='ws_open')
        except Exception as exc:
            if 'Element is not clickable' in str(exc):
                NotifierPage.wait(self, base_id='ws_closed')
                NotifierPage.wait(self, base_id='ws_open')
            else:
                raise
        else:
            self.browser.implicitly_wait(1)
            try:
                NotifierPage.wait(self,
                                  timeout=1,
                                  base_id='ws_closed',
                                  retries=0)
            except TimeoutException:
                pass  # ws closed dialog may not exist
            finally:
                self.browser.implicitly_wait(TMO)

    def find_library_button(self, name, delay=0):
        path = "//table[(@id='objtypetable')]//td[text()='%s']" % name
        for retry in range(5):
            try:
                element = WebDriverWait(self.browser, TMO).until(
                    lambda browser: browser.find_element(By.XPATH, path))
            except TimeoutException as err:
                logging.warning(str(err))
            else:
                break
        else:
            raise err
        if delay:
            time.sleep(delay)
        return element

    def find_object_button(self, name, delay=0):
        path = "//div[@id='otree_pane']//li[(@path='%s')]//a" % name
        for retry in range(5):
            try:
                element = WebDriverWait(self.browser, TMO).until(
                    lambda browser: browser.find_element(By.XPATH, path))
            except TimeoutException as err:
                logging.warning(str(err))
            else:
                break
        else:
            raise err
        if delay:
            time.sleep(delay)
        return element

    def do_command(self, cmd, timeout=TMO, ack=True):
        """ Execute a command. """
        self.command = cmd
        self('submit').click()
        if ack:
            NotifierPage.wait(self, timeout, base_id='command')

    def close_workspace(self, commit=False):
        """ Close the workspace page. Returns :class:`ProjectsPage`. """
        if commit:
            self.commit_project()
        self.browser.execute_script('openmdao.project.closeWebSockets();')
        NotifierPage.wait(self, base_id='ws_closed')
        self('project_menu').click()
        self('close_button').click()

        from project import ProjectsPage
        return ProjectsPage.verify(self.browser, self.port)

    def attempt_to_close_workspace(self, expectDialog, confirm):
        """ Close the workspace page. Returns :class:`ProjectsPage`. """
        self('project_menu').click()
        self('close_button').click()

        #if you expect the "close without saving?" dialog
        if expectDialog:
            dialog = ConfirmationPage(self)
            if confirm:  # close without saving
                self.browser.execute_script(
                    'openmdao.project.closeWebSockets();')
                NotifierPage.wait(self)
                dialog.click_ok()
                from project import ProjectsPage
                return ProjectsPage.verify(self.browser, self.port)
            else:  # return to the project, intact.
                dialog.click_cancel()
        else:  # no unsaved changes
            from project import ProjectsPage
            return ProjectsPage.verify(self.browser, self.port)

    def open_editor(self):
        """ Open code editor.  Returns :class:`EditorPage`. """
        self('tools_menu').click()
        self('editor_button').click()
        self.browser.switch_to_window('Code Editor')
        return EditorPage.verify(self.browser, self.port)

    def get_files(self):
        """ Return names in the file tree. """
        self('files_tab').click()
        WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element(By.ID, 'ftree_pane'))
        # FIXME: absolute delay for tree population.
        time.sleep(1)
        file_items = self.browser.find_elements(*self.locators["files"])
        file_names = []
        for i in range(len(file_items)):
            for retry in range(10):  # This has had issues...
                try:
                    file_names.append(
                        self.browser.find_elements(
                            *self.locators["files"])[i].text.strip())
                except StaleElementReferenceException:
                    logging.warning(
                        'get_files: StaleElementReferenceException')
                else:
                    break
        return file_names

    def add_file(self, file_path):
        """ Read in `file_path` """
        if file_path.endswith('.pyc'):
            file_path = file_path[:-1]

        self('files_tab').click()
        self('file_menu').click()
        self('add_button').click()

        self.file_chooser = file_path
        time.sleep(1)  # Some extra time for the file tree update.
        self.find_file(os.path.basename(file_path))  # Verify added.
        time.sleep(1)  # Some extra time for the library update.

    def add_file_to_folder(self, folder_path, file_path):
        """ Read in `file_path` """
        if file_path.endswith('.pyc'):
            file_path = file_path[:-1]

        self('files_tab').click()
        element = self.find_file(folder_path)
        chain = ActionChains(self.browser)
        chain.context_click(element).perform()
        time.sleep(0.5)
        self('file_add').click()
        time.sleep(0.5)

        self.file_chooser = file_path
        time.sleep(1)  # Some extra time for the file tree update.

    def new_file_dialog(self):
        """ bring up the new file dialog """
        self('files_tab').click()
        self('file_menu').click()
        self('newfile_button').click()
        return ValuePrompt(self.browser, self.port)

    def new_folder_dialog(self):
        """ bring up the new folder dialog """
        self('files_tab').click()
        self('file_menu').click()
        self('newfolder_button').click()
        return ValuePrompt(self.browser, self.port)

    def new_file(self, filename):
        """ Make a new empty file `filename`. """
        page = self.new_file_dialog()
        page.set_value(filename)
        NotifierPage.wait(self)  # Wait for creation to complete.

    def new_folder(self, foldername):
        """ Make a new empty folder `foldername`. """
        page = self.new_folder_dialog()
        page.set_value(foldername)

    def find_file(self, filename, tmo=TMO):
        """ Return element corresponding to `filename`. """
        xpath = "//a[(@path='/%s')]" % filename
        return WebDriverWait(
            self.browser,
            tmo).until(lambda browser: browser.find_element_by_xpath(xpath))

    def edit_file(self, filename, dclick=True):
        """ Edit `filename` via double-click or context menu. """
        self('files_tab').click()
        element = self.find_file(filename)
        chain = ActionChains(self.browser)
        if dclick:  # This has had issues...
            for i in range(10):
                try:
                    chain.double_click(element).perform()
                except StaleElementReferenceException:
                    logging.warning(
                        'edit_file: StaleElementReferenceException')
                    element = self.find_file(filename, 1)
                    chain = ActionChains(self.browser)
                else:
                    break
        else:
            chain.context_click(element).perform()
            self('file_edit').click()
        self.browser.switch_to_window('Code Editor')
        return EditorPage.verify(self.browser, self.port)

    def view_file(self, filename):
        """ View image `filename` in another window via context menu. """
        self('files_tab').click()
        element = self.find_file(filename)
        chain = ActionChains(self.browser)
        chain.context_click(element).perform()
        self('file_view').click()
        self.browser.switch_to_window(self.browser.window_handles[-1])
        return BasePageObject.verify(self.browser, self.port)

    def view_image(self, filename, dclick=True):
        """ View image `filename` via double-click or context menu. """
        self('files_tab').click()
        element = self.find_file(filename)
        chain = ActionChains(self.browser)
        if dclick:  # This has had issues...
            for i in range(10):
                try:
                    chain.double_click(element).perform()
                except StaleElementReferenceException:
                    logging.warning(
                        'edit_file: StaleElementReferenceException')
                    element = self.find_file(filename, 1)
                    chain = ActionChains(self.browser)
                else:
                    break
        else:
            chain.context_click(element).perform()
            self('file_image').click()
        self.browser.switch_to_window(self.browser.window_handles[-1])
        return ImagesPage.verify(self.browser, self.port)

    def view_geometry(self, filename, dclick=True):
        """ View geometry `filename` via double-click or context menu. """
        self('files_tab').click()
        element = self.find_file(filename)
        chain = ActionChains(self.browser)
        if dclick:  # This has had issues...
            for i in range(10):
                try:
                    chain.double_click(element).perform()
                except StaleElementReferenceException:
                    logging.warning(
                        'edit_file: StaleElementReferenceException')
                    element = self.find_file(filename, 1)
                    chain = ActionChains(self.browser)
                else:
                    break
        else:
            chain.context_click(element).perform()
            self('file_geometry').click()
        self.browser.switch_to_window(self.browser.window_handles[-1])
        return GeometryPage.verify(self.browser, self.port)

    def delete_file(self, filename, confirm=True):
        """ Delete `filename`. """
        self('files_tab').click()
        element = self.find_file(filename)
        chain = ActionChains(self.browser)
        chain.context_click(element).perform()
        time.sleep(0.5)
        self('file_delete').click()
        time.sleep(0.5)
        page = ConfirmationPage(self)
        if confirm:
            page.click_ok()
        else:
            page.click_cancel()

    def delete_files(self, file_paths, confirm=True):
        """ Delete all the files in the list `file_paths` """

        # need select all the files given in file_paths
        self('files_tab').click()
        for filename in file_paths:
            element = self.find_file(filename)
            chain = ActionChains(self.browser)
            #Mac OSX does not use CONTROL key
            if sys.platform == 'darwin':
                chain.key_down(Keys.SHIFT).click(element).key_up(
                    Keys.SHIFT).perform()
            else:
                chain.key_down(Keys.CONTROL).click(element).key_up(
                    Keys.CONTROL).perform()

        self('files_tab').click()
        self('file_menu').click()
        self('delete_files_button').click()
        page = ConfirmationPage(self)
        if confirm:
            page.click_ok()
        else:
            page.click_cancel()

    def expand_folder(self, filename):
        """ Expands `filename`. """
        self('files_tab').click()
        xpath = "//div[@id='ftree_pane']//a[(@path='/%s')]/../ins" % filename
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(1)  # Wait for cute animation.

    def rename_file(self, old, new):
        """ Rename `old` to `new`. """
        self('files_tab').click()
        element = self.find_file(old)
        chain = ActionChains(self.browser)
        chain.context_click(element).perform()
        self('file_rename').click()
        prompt = ValuePrompt(self.browser, self.port)
        prompt.set_value(new)

    def toggle_files(self, filename=None):
        """ Toggle display of hidden files.
            Use context menu of `filename` if provided,
            otherwise use the context menu of the file tree pane.
        """
        self('files_tab').click()
        time.sleep(0.5)
        if filename:
            element = self.find_file(filename)
            chain = ActionChains(self.browser)
            chain.context_click(element).perform()
            time.sleep(0.5)
            self('file_toggle').click()
            time.sleep(0.5)
        else:
            element = self.browser.find_element(By.ID, 'ftree_pane')
            chain = ActionChains(self.browser)
            chain.context_click(element).perform()
            time.sleep(0.5)
            self('ftree_toggle_files_button').click()
            time.sleep(0.5)

    def commit_project(self, comment='no comment'):
        """ Commit current project. """
        self('project_menu').click()
        self('commit_button').click()
        page = ValuePrompt(self.browser, self.port)
        page.set_value(comment)
        NotifierPage.wait(self)

    def revert_project(self):
        """ Revert current project. """
        self('project_menu').click()
        self('revert_button').click()
        page = ConfirmationPage(self)
        page.click_ok()
        return WorkspacePage.verify(self.browser, self.port)

    def reload_project(self):
        """ Reload current project. """
        self('project_menu').click()
        self('reload_button').click()
        WorkspacePage.verify(self.browser, self.port)

    def get_objects_attribute(self, attribute, visible=False):
        """ Return list of `attribute` values for all objects. """
        WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element(By.ID, 'otree_pane'))
        object_elements = self.browser.find_elements(*self.locators["objects"])
        values = []
        for element in object_elements:
            if not visible or element.is_displayed():
                values.append(element.get_attribute(attribute))
        return values

    def select_objects_view(self, tree_name):
        """ Select the object tree view ('Components' or 'Workflow)'. """
        self('objects_selector').value = tree_name

    def select_object(self, component_name):
        """ Select `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()

    def expand_object(self, component_name):
        """ Expands `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//ins" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(1)  # Wait for cute animation.

    def show_dataflow(self, component_name=None):
        """ Show dataflow of `component_name`. """
        if component_name is None:
            self('dataflow_tab').click()
            return

        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(0.5)
        # Try to recover from context menu not getting displayed.
        for retry in range(3):
            chain = ActionChains(self.browser)
            chain.context_click(element).perform()
            try:
                self('obj_dataflow').click()
                break
            except TimeoutException:
                if retry >= 2:
                    raise

    def show_workflow(self, component_name=None):
        """ Show workflow of `component_name`. """
        if component_name is None:
            self('workflow_tab').click()
            return

        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(0.5)
        # Try to recover from context menu not getting displayed.
        for retry in range(3):
            chain = ActionChains(self.browser)
            chain.context_click(element).perform()
            try:
                self('obj_workflow').click()
                break
            except TimeoutException:
                if retry >= 2:
                    raise

    def show_properties(self):
        """ Display properties. """
        # This has had some odd failures where the tab is highlighted as if
        # hovering over it, yet the Library tab is still the selected one.
        for retry in range(5):
            try:
                self('properties_tab').click()
                WebDriverWait(
                    self.browser,
                    1).until(lambda browser: self('props_header').is_visible)
            except TimeoutException:
                if retry:
                    logging.warning('TimeoutException in show_properties')
            else:
                break
        else:
            raise RuntimeError('Too many TimeoutExceptions')

    def get_properties(self, name, prefix=None):
        self.show_properties()
        self.show_dataflow()
        obj = self.get_dataflow_figure(name, prefix=prefix)
        chain = ActionChains(self.browser)
        chain.click(obj.root)
        chain.perform()
        time.sleep(0.5)
        return (self.props_header, self.props_inputs, self.props_outputs)

    def show_library(self):
        """ Display library. """
        # For some reason the first try never works, so the wait is set
        # low and we expect to retry at least once.
        for retry in range(5):
            try:
                self('library_tab').click()
                WebDriverWait(
                    self.browser,
                    1).until(lambda browser: self('library_search').is_visible)
            except TimeoutException:
                if retry:
                    logging.warning('TimeoutException in show_library')
            else:
                break
        else:
            raise RuntimeError('Too many TimeoutExceptions')

    def set_library_filter(self, filter):
        """ Set the search filter text. """
        for retry in range(3):  # This has had issues...
            try:
                self.library_search = filter + Keys.RETURN
            except StaleElementReferenceException:
                logging.warning('set_library_filter:'
                                ' StaleElementReferenceException')
            else:
                break
        time.sleep(1)  # Wait for display update.

    def clear_library_filter(self):
        """ Clear the search filter via the 'X' button. """
        self('library_clear').click()
        time.sleep(0.5)  # Wait for display update.

    def get_object_types(self):
        """ Return displayed object types. """
        xpath = "//table[(@id='objtypetable')]//td"
        return [
            element.text
            for element in self.browser.find_elements(By.XPATH, xpath)
        ]

    def get_library_searches(self):
        """ Return stored library search terms. """
        searches = []

        self.library_search = 'searches'
        for menu in self.browser.find_elements(By.CLASS_NAME,
                                               'ui-autocomplete'):
            items = menu.find_elements(By.CLASS_NAME, 'ui-menu-item')
            searches = [item.text for item in items]
            if len(searches) > 0 and searches[0] == 'In Project':
                break

        self.clear_library_filter()
        return searches

    def get_library_item(self, item_name):
        """ Return element for library item `item_name`. """
        xpath = "//table[(@id='objtypetable')]//td[(@modpath='%s')]" % item_name
        library_item = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        for retry in range(3):
            try:
                WebDriverWait(
                    self.browser,
                    TMO).until(lambda browser: library_item.is_displayed())
            except StaleElementReferenceException:
                if retry < 2:
                    logging.warning('get_library_item:'
                                    ' StaleElementReferenceException')
                    library_item = WebDriverWait(self.browser, TMO).until(
                        lambda browser: browser.find_element_by_xpath(xpath))
                else:
                    raise
            else:
                break
        return library_item

    def view_library_item_docs(self, item_name):
        chain = ActionChains(self.browser)
        current_windows = set(self.browser.window_handles)
        self.show_library()
        item = self.get_library_item(item_name)
        chain.context_click(item).perform()
        self("library_item_docs").click()
        new_windows = set(self.browser.window_handles) - current_windows
        docs_window = list(new_windows)[0]

        return docs_window

    def add_library_item_to_dataflow(self,
                                     item_name,
                                     instance_name,
                                     target_name=None,
                                     check=True,
                                     offset=None,
                                     prefix=None,
                                     args=None):
        """ Add component `item_name`, with name `instance_name`. """
        library_item = self.get_library_item(item_name)

        if target_name:
            target = self.get_dataflow_figure(
                target_name).get_drop_targets()[0]
            offset = offset or (2, 2)  # top left corner of target content area
        else:
            xpath = "//*[@id='-dataflow']"
            target = WebDriverWait(self.browser, TMO).until(
                lambda browser: browser.find_element_by_xpath(xpath))
            offset = offset or (50, 50)

        for retry in range(3):
            try:
                chain = ActionChains(self.browser)
                if False:
                    chain.drag_and_drop(library_item, target)
                else:
                    chain.click_and_hold(library_item)
                    chain.move_to_element_with_offset(target, offset[0],
                                                      offset[1])
                    chain.release(None)
                chain.perform()
            except StaleElementReferenceException:
                if retry < 2:
                    logging.warning('add_library_item_to_dataflow:'
                                    ' StaleElementReferenceException')
                    library_item = self.get_library_item(item_name)
                    target = WebDriverWait(self.browser, TMO).until(
                        lambda browser: browser.find_element_by_xpath(xpath))
                else:
                    raise
            else:
                break

        page = ArgsPrompt(self.browser, self.port)
        argc = page.argument_count()
        page.set_name(instance_name)
        if argc > 0:
            if args is not None:
                for i, arg in enumerate(args):
                    page.set_argument(i, arg)
            page.click_ok()

        # Check that the prompt is gone so we can distinguish a prompt problem
        # from a dataflow update problem.
        time.sleep(0.25)
        self.browser.implicitly_wait(1)  # We don't expect to find anything.
        try:
            eq(len(self.browser.find_elements(*page('prompt')._locator)), 0)
        finally:
            self.browser.implicitly_wait(TMO)

        retval = None
        if check:  # Check that it's been added.
            retval = WebDriverWait(
                self.browser,
                TMO).until(lambda browser: self.get_dataflow_figure(
                    instance_name, prefix))
        return retval

    def fill_slot_from_library(self, slot, classname, args=None):
        """ Fill slot with `classname` instance from library. """
        for retry in range(3):
            try:
                button = self.find_library_button(classname)
                chain = ActionChains(self.browser)
                chain.move_to_element(button)
                chain.click_and_hold(button)
                chain.move_to_element(slot.root)
                chain.release(None)
                chain.perform()
            except StaleElementReferenceException:
                if retry < 2:
                    logging.warning('fill_slot_from_library:'
                                    'StaleElementReferenceException')
                else:
                    raise
            else:
                break

        # Handle arguments for the slotted class
        page = ArgsPrompt(self.browser, self.port)
        argc = page.argument_count()
        if argc > 0:
            if args is not None:
                for i, arg in enumerate(args):
                    page.set_argument(i, arg)
            page.click_ok()

        # Check that the prompt is gone so we can distinguish a prompt problem
        # from a dataflow update problem.
        time.sleep(0.5)
        self.browser.implicitly_wait(1)  # We don't expect to find anything.
        try:
            eq(len(self.browser.find_elements(*page('prompt')._locator)), 0)
        finally:
            self.browser.implicitly_wait(TMO)

    def get_dataflow_figures(self):
        """ Return dataflow figure elements. """
        return find_dataflow_figures(self)

    def get_dataflow_figure(self, name, prefix=None, retries=5):
        """ Return :class:`DataflowFigure` for `name`. """
        return find_dataflow_figure(self, name, prefix, retries)

    def get_dataflow_component_names(self):
        """ Return names of dataflow components. """
        return find_dataflow_component_names(self)

    def connect(self, src, dst):
        """ Return :class:`ConnectionsPage` for connecting `src` to `dst`. """
        chain = ActionChains(self.browser)
        chain.click_and_hold(src.output_port)
        # Using root rather than input_port since for some reason
        # even using a negative Y offset can select the parent's input.
        chain.move_to_element(dst.input_port)
        chain.release(None)
        chain.perform()
        parent, dot, srcname = src.pathname.rpartition('.')
        parent, dot, dstname = dst.pathname.rpartition('.')
        editor_id = 'ConnectionsFrame-%s' % (parent)
        editor_id = editor_id.replace('.', '-')
        return ConnectionsPage(self.browser, self.port, (By.ID, editor_id))

    def replace(self, name, classname, confirm=True):
        """ Replace `name` with an instance of `classname`. """
        library_item = self.get_library_item(classname)
        target = self.get_dataflow_figure(name).root

        chain = ActionChains(self.browser)
        chain.click_and_hold(library_item)
        chain.move_to_element(target)
        chain.release(None)
        chain.perform()

        dialog = ConfirmationPage(self)
        if confirm:
            dialog.click_ok()
        else:
            dialog.click_cancel()

    def add_object_to_workflow(self, obj_path, target_name):
        """ Add `obj_path` object to `target_name` in workflow. """
        for retry in range(3):
            try:
                items = obj_path.split('.')
                parent = items[:-1]
                comp = items[-1]
                obj = self.get_dataflow_figure(comp, parent)

                target = self.find_object_button(target_name)

                chain = ActionChains(self.browser)
                chain.drag_and_drop(obj.root, target)
                chain.perform()

                #obj = self.find_object_button(obj_path)
                #workflow = self.get_workflow_figure(target_name)
                #flow_fig = workflow.flow
                #chain = ActionChains(self.browser)
                #chain.move_to_element(obj)
                #chain.click_and_hold(obj)
                #chain.move_to_element(flow_fig)
                #chain.move_by_offset(2, 1)
                #chain.release(None)
                #chain.perform()
            except StaleElementReferenceException:
                if retry < 2:
                    logging.warning('add_object_to_workflow:'
                                    ' StaleElementReferenceException')
                else:
                    raise
            else:
                break

    def add_object_to_workflow_figure(self,
                                      obj_path,
                                      target_name,
                                      target_page=None):
        """ Add `obj_path` object to `target_name` in workflow diagram. """

        if target_page is None:
            target_page = self

        for retry in range(3):
            try:
                items = obj_path.split('.')
                parent = items[:-1]
                comp = items[-1]
                obj = self.get_dataflow_figure(comp, parent)

                workflow = target_page.get_workflow_figure(target_name)
                flow_fig = workflow.flow

                chain = ActionChains(self.browser)
                chain.drag_and_drop(obj.root, flow_fig)
                chain.perform()
            except StaleElementReferenceException:
                if retry < 2:
                    logging.warning('add_object_to_workflow_figure:'
                                    ' StaleElementReferenceException')
                else:
                    raise
            else:
                break

    def get_workflow_figures(self):
        """ Return workflow figure elements. """
        return find_workflow_figures(self)

    def get_workflow_component_figures(self):
        """ Return workflow component figure elements. """
        return find_workflow_component_figures(self)

    def get_workflow_figure(self, name, prefix=None, retries=5):
        """ Return :class:`WorkflowFigure` for `name`. """
        return find_workflow_figure(self, name, prefix, retries)

    def get_workflow_component_figure(self, name, prefix=None, retries=5):
        """ Return :class:`WorkflowComponentFigure` for `name`. """
        return find_workflow_component_figure(self, name, prefix, retries)

    def show_log(self):
        """ Open log viewer.  Returns :class:`LogViewer`. """
        self('tools_menu').click()
        self('log_button').click()
        return LogViewer.verify(self.browser, self.port)

    def hide_left(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-west-open')
        toggler.click()

    def show_left(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-west-closed')
        toggler.click()

    def hide_right(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-east-open')
        toggler.click()

    def show_right(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-east-closed')
        toggler.click()

    def hide_console(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-south-open')
        toggler.click()

    def show_console(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-south-closed')
        toggler.click()

    def drag_element_to(self, element, drag_to, center):
        '''Drag one element over to another element'''
        chain = ActionChains(self.browser)
        chain.move_to_element(element)
        chain.click_and_hold(element)
        if center:
            # move to center of element
            chain.move_to_element(drag_to)
        else:
            # move to offset from top left of element
            chain.move_to_element_with_offset(drag_to, 2, 2)
        chain.perform()
        return chain

    def release(self, chain):
        '''The drop part of the ActionChain when doing drag and drop'''
        chain.release(on_element=None).perform()
        time.sleep(0.5)  # Pacing for diagram update.

    def replace_driver(self, assembly_name, driver_type):
        ''' find 'driver_type' in the library, drag and drop it on to
            the driver figure of the 'assembly_name'
        '''
        newdriver = self.find_library_button(driver_type)
        assembly = self.get_dataflow_figure(assembly_name)
        driver_element = self.get_dataflow_figure('driver')

        div = driver_element.get_drop_targets()[0]
        chain = self.drag_element_to(newdriver, div, True)
        self.check_highlighting(
            driver_element('content_area').element, True,
            "Driver's content_area")
        self.release(chain)

        # brings up a confirm dialog for replacing the existing driver.
        dialog = ConfirmationPage(assembly)
        dialog.click_ok()

    def resize_editor(self, editor):
        '''ensure that the editor is not covering the library
        (or else we cannot drag things from it!)'''
        browser = self.browser

        page_width = browser.get_window_size()['width']

        lib_tab = self('library_tab').find_element_by_xpath('..')
        lib_width = lib_tab.size['width']
        lib_position = lib_tab.location['x']

        dialog_title = editor('dialog_title').find_element_by_xpath('../..')
        dialog_width = dialog_title.size['width']
        dialog_position = dialog_title.location['x']

        # how much overlap do we have?
        overlap = lib_position - (dialog_position + dialog_width)

        if overlap < 0:  # we are overlapping
            # check to see if we have enough room to move out of the way
            if page_width < dialog_width + lib_width:
                # not enough, need to rezize the editor

                # look for the resize handle
                sibblings = dialog_title.find_elements_by_xpath('../../div')
                handle = None
                for sib in sibblings:
                    if "ui-resizable-se" in sib.get_attribute('class'):
                        handle = sib

                # do the resizing
                chain = ActionChains(browser)
                chain.click_and_hold(handle)
                # we can resize editor down to 425px, any less and we cover drop targets
                chain.move_by_offset(450 - dialog_width, 0).perform()
                # must click because release is not working. why? I do not know.
                chain.click().perform()
                chain.release(None).perform()

                # recalculate the overlap
                dialog_title = editor('dialog_title').find_element_by_xpath(
                    '../..')
                dialog_width = dialog_title.size['width']
                dialog_position = dialog_title.location['x']
                overlap = lib_position - (dialog_position + dialog_width)

            # We are good, move out!
            chain = ActionChains(browser)
            chain.click_and_hold(editor('dialog_title').element)
            chain.move_by_offset(overlap, 0).perform()
            # must click because release is not working. why? I do not know.
            chain.click().perform()
            chain.release(None).perform()

            # recalculate the overlap
            dialog_title = editor('dialog_title').find_element_by_xpath(
                '../..')
            dialog_width = dialog_title.size['width']
            dialog_position = dialog_title.location['x']
            overlap = lib_position - (dialog_position + dialog_width)

            if overlap < 0:
                # we still have a problem.
                eq(
                    True, False,
                    "Could not move or rezise the editor dialog so it is not "
                    "overlapping the library. The browser window is too small")

    def get_dataflow_fig_in_globals(self, name):
        '''Find the named dataflow fig in the global dataflow editor'''
        all_figs = self.get_dataflow_figures()
        for fig in all_figs:
            location = fig.get_parent().get_attribute('id')
            if location == "top-dataflow":
                return fig

        return None

    def put_element_on_grid(self, type_str):
        '''find 'type_str' in the Library, drag and drop it onto the grid'''
        browser = self.browser

        grid = browser.find_element_by_xpath('//div[@id="-dataflow"]')

        for retry in range(3):
            try:
                objtype = self.find_library_button(type_str)
                chain = ActionChains(browser)
                chain.click_and_hold(objtype)
                chain.move_to_element_with_offset(grid, 10, 10).perform()
            except StaleElementReferenceException:
                if retry < 2:
                    logging.warning(
                        'put_element_on_grid %s:'
                        ' StaleElementReferenceException', type_str)
                else:
                    raise
            else:
                break

        self.check_highlighting(grid, True, "Grid")
        self.release(chain)

        # deal with the modal dialog
        name = NameInstanceDialog(self).create_and_dismiss()

        # make sure it is on the grid
        self.ensure_names_in_workspace(
            [name], "Dragging '" + type_str +
            "' to grid did not produce a new element on page")

        return name

    def drag_and_drop(self, element, target, should_drop, message='Target'):
        '''Drag and drop an element onto a target'''
        chain = self.drag_element_to(element, target, True)
        chain.move_by_offset(25, 0).perform()
        time.sleep(1.0)  # give it a second to update the figure
        self.check_highlighting(target,
                                should_highlight=should_drop,
                                message=message)
        self.release(chain)

    def ensure_names_in_workspace(self, names, message=None):
        """ensures the list of element names in included in the workspace"""

        allnames = self.get_dataflow_component_names()

        # sometimes does not load all of the names for some reason.
        # Reloading seems to fix the problem
        try_reload = False
        for name in names:
            if not name in allnames:
                try_reload = True
        if try_reload:
            time.sleep(.1)
            allnames = self.get_dataflow_component_names()

        # now we will assert that the elements that we added appear on the page
        for name in names:
            eq(name in allnames, True, '%s: %s' % (message, name))

    def check_highlighting(self,
                           element,
                           should_highlight=True,
                           message='Element'):
        '''check to see that the background-color of the element is highlighted
        '''
        if 'SlotFigure' in element.get_attribute('class'):
            # a slot figure is a div containing a ul element (the context menu) and
            # one or more svg elements, each of which contains a rect and two texts
            # the last rect fill style is what we need to check for highlighting
            rect = element.find_elements_by_css_selector('svg rect')[-1]
            style = rect.get_attribute('style')
        else:
            style = element.get_attribute('style')
        highlighted = ('background-color: rgb(207, 214, 254)' in style) \
                    or ('highlighted.png' in style) \
                    or ('fill: #cfd6fe' in style)
        eq(
            highlighted, should_highlight, message +
            (' did not highlight (and should have) '
             if should_highlight else ' highlighed (and should not have) ') +
            'when dragging a dropable element to it')
Esempio n. 18
0
 def repository_name_input(self):
     locator = CreateRepositoryPageLocator.repository_name_input
     return InputElement(driver=self.driver, locator=locator)
Esempio n. 19
0
class ProjectsPage(BasePageObject):
    """ Displays list of projects. """

    url = '/projects'
    title_prefix = 'Projects'

    welcome_text = TextElement((By.XPATH, "//h3/strong"))
    search_input = InputElement(
        (By.XPATH, "//div[@id='project_table_filter']/label/input"))
    import_button = ButtonElement((By.LINK_TEXT, 'Import Project'))
    new_button = ButtonElement((By.LINK_TEXT, 'New Project'))
    logout_button = ButtonElement((By.LINK_TEXT, 'Exit'))

    def new_project(self):
        """ Clicks the 'new' button. Returns :class:`NewDialog`. """
        self('new_button').click()
        page = NewDialog(self.browser, self.port, (By.ID, "newProjectModal"))
        WebDriverWait(
            self.browser,
            TMO).until(lambda browser: page.modal_title[:11] == 'New Project')
        return page

    def import_project(self):
        """ Clicks the 'import' button. Returns :class:`ImportDialog`. """
        self('import_button').click()
        page = ImportDialog(self.browser, self.port,
                            (By.ID, "importProjectModal"))
        time.sleep(1)  # Wait for silly fade-in.
        return page

    def logout(self):
        """
        Clicks the 'logout' button. Returns :class:`LoginPage`.

        ..warning::

            Since login isn't done anymore, this actually results in
            exiting the server.

        """
        self('logout_button').click()
        from login import LoginPage
        return LoginPage.verify(self.browser, self.port)

    def contains(self, project_name, expected=True):
        """ Returns True if `project_name` is in the list of projects. """
        if expected:
            self.search_input = project_name
            elements = self.browser.find_elements_by_link_text(project_name)
        else:
            self.browser.implicitly_wait(1)  # Not expecting to find anything.
            try:
                self.search_input = project_name  # No search input if no projects.
                elements = self.browser.find_elements_by_link_text(
                    project_name)
            except TimeoutException:
                elements = []
            finally:
                self.browser.implicitly_wait(TMO)
        return len(elements) > 0

    def open_project(self, project_name):
        """ Clicks the named link. Returns :class:`WorkspacePage`. """
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element.click()
        from workspace import WorkspacePage
        return WorkspacePage.verify(self.browser, self.port)

    def edit_project(self, project_name):
        """ Clicks the 'edit' button. Returns :class:`EditDialog`. """
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element = element.find_element_by_xpath('../../td[6]/a')
        element.click()

        page = EditDialog(self.browser, self.port, (By.ID, "editProjectModal"))
        time.sleep(1)  # Wait for silly fade-in.
        return page

    def export_project(self, project_name):
        """ Clicks the 'export' button. Returns :class:`ExportDialog`. """
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element = element.find_element_by_xpath('../../td[6]/form[2]/button')
        element.click()

    def delete_project(self, project_name):
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element = element.find_element_by_xpath('../../td[6]/form[1]/button')
        element.click()

        delete_dialog = DeleteDialog(self.browser, self.port,
                                     (By.XPATH, "/html/body/div[2]"))
        time.sleep(1)  # Wait for silly fade-in.
        delete_dialog.submit()
        time.sleep(1)  # Wait for silly fade-out.

    def delete_projects(self, project_filter, verbose=False):
        """ Removes all projects with 'test project' in the name. """
        self.search_input = project_filter + '\n'
        elements = self.browser.find_elements_by_partial_link_text(
            project_filter)
        while len(elements) > 0:
            for i in range(len(elements)):
                element = WebDriverWait(self.browser, TMO).until(
                    lambda browser: browser.find_element_by_partial_link_text(
                        project_filter))

                project_name = element.text
                self.delete_project(project_name)
                if verbose:
                    print >> sys.stderr, 'Deleted', project_name
                self.search_input = project_filter + '\n'
            # there may be more that were previously hidden due to the row limit
            elements = self.browser.find_elements_by_partial_link_text(
                project_filter)

    def get_project_metadata(self, project_name):
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))

        elements = element.find_elements_by_xpath('../../td')

        metadata = {
            "name": elements[0].text,
            "description": elements[1].text,
            "version": elements[2].text,
            "created": elements[3].text,
            "last_saved": elements[4].text,
        }

        return metadata
Esempio n. 20
0
 def password_input(self):
     locator = LoginPageLocator.password_input
     return InputElement(driver=self.driver, locator=locator)
Esempio n. 21
0
 def input_box(self):
     locator = TwoWayAuthenticationPageLocator.input_box
     return InputElement(driver=self.driver, locator=locator)
Esempio n. 22
0
class EditorPage(BasePageObject):
    """ Code editor window. """

    title_prefix = 'OpenMDAO:'

    # Left side.
    file_menu = ButtonElement((By.XPATH, '/html/body/div/div/nav2/ul/li/a'))
    newfile_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/nav2/ul/li/ul/li[1]/a'))
    newfolder_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/nav2/ul/li/ul/li[2]/a'))
    add_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/nav2/ul/li/ul/li[3]/a'))

    # File context menu.
    file_create = ButtonElement((By.XPATH, "//a[(@rel='createFile')]"))
    file_add = ButtonElement((By.XPATH, "//a[(@rel='addFile')]"))
    file_folder = ButtonElement((By.XPATH, "//a[(@rel='createFolder')]"))
    file_rename = ButtonElement((By.XPATH, "//a[(@rel='renameFile')]"))
    file_view = ButtonElement((By.XPATH, "//a[(@rel='viewFile')]"))
    file_edit = ButtonElement((By.XPATH, "//a[(@rel='editFile')]"))
    file_import = ButtonElement((By.XPATH, "//a[(@rel='importFile')]"))
    file_exec = ButtonElement((By.XPATH, "//a[(@rel='execFile')]"))
    file_delete = ButtonElement((By.XPATH, "//a[(@rel='deleteFile')]"))
    file_toggle = ButtonElement((By.XPATH, "//a[(@rel='toggle')]"))

    # Right side.
    editor_new_button = ButtonElement((By.ID, 'code_pane-uiBar-new'))
    editor_save_button = ButtonElement((By.ID, 'code_pane-uiBar-save'))
    editor_find_button = ButtonElement((By.ID, 'code_pane-uiBar-find'))
    editor_replace_button = ButtonElement((By.ID, 'code_pane-uiBar-replace'))
    editor_replaceAll_button = ButtonElement(
        (By.ID, 'code_pane-uiBar-replaceAll'))
    editor_undo_button = ButtonElement((By.ID, 'code_pane-uiBar-undo'))
    editor_overwrite_button = ButtonElement((By.ID, 'code_pane-overwrite'))
    editor_cancel_button = ButtonElement((By.ID, 'code_pane-cancel'))

    editor_label = TextElement((By.ID, 'code_pane-label'))

    file_chooser = InputElement((By.ID, 'filechooser'))

    def __init__(self, browser, port):
        super(EditorPage, self).__init__(browser, port)

        self.locators = {}
        self.locators["files"] = (
            By.XPATH, "//div[@id='file_pane']//a[@class='file ui-draggable']")

    def get_code(self):
        return self.browser.execute_script(
            "return openmdao.frames.code_pane.editor.getValue()")

    def get_tab_label(self):
        label = self.browser.execute_script(
            "return openmdao.frames.code_pane.currentTablabel()")
        return ''.join(label.split('*'))  # ignore changed / unchanged status

    def get_files(self):
        """ Return names in the file tree. """
        WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element(By.ID, 'file_pane'))
        # FIXME: absolute delay for tree population.
        time.sleep(1)
        file_items = self.browser.find_elements(*self.locators["files"])
        file_names = []
        for i in range(len(file_items)):
            for retry in range(10):  # This has had issues...
                try:
                    file_names.append(
                        self.browser.find_elements(
                            *self.locators["files"])[i].text.strip())
                except StaleElementReferenceException:
                    logging.warning(
                        'get_files: StaleElementReferenceException')
                else:
                    break
        return file_names

    def add_file(self, file_path):
        """ Read in `file_path` """
        if file_path.endswith('.pyc'):
            file_path = file_path[:-1]

        self('file_menu').click()
        self('add_button').click()

        self.file_chooser = file_path

    def add_files(self, *file_paths):
        """ Read in multiple 'file_path's
            FIXME: doesn't work, see elements._InputElement
            Have to use multiple calls to add_file for now
        """
        self('file_menu').click()
        self('add_button').click()
        self('file_chooser').set_values(*file_paths)

    def new_file_dialog(self):
        """ bring up the new file dialog """
        self('file_menu').click()
        self('newfile_button').click()
        return ValuePrompt(self.browser, self.port)

    def find_text(self, text):
        #click the 'find' button, and enter text. Not yet functional
        self('editor_find_button').click()
        alert = self.browser.switch_to_alert()
        chain = ActionChains(alert)
        chain.send_keys(text).perform()
        chain.send_keys(Keys.RETURN).perform()
        return

    def replace_text(self, old_text, new_text, replace_all=False):
        #click the 'replace' or 'replace all 'button,
        # and enter text to find and replace. Not yet functional
        if replace_all:
            self('editor_replace_button').click()
        else:
            self('editor_replaceAll_button').click()
        return

    def undo(self):
        #click the 'undo' button
        self('editor_undo_button').click()
        return

    def new_file(self, filename, code, check=True):
        """ Make a new file `filename` with contents `code`. """
        page = self.new_file_dialog()
        page.set_value(filename)

        NotifierPage.wait(self)  # Wait for creation to complete.

        # Switch to editor textarea
        code_input_element = self.get_text_area()

        # Go to the bottom of the code editor window
        for i in range(4):
            code_input_element.send_keys(Keys.ARROW_DOWN)
        # Type in the code.
        code_input_element.send_keys(code)

        self.save_document(check=check)

    def edit_file(self, filename, dclick=True):
        """ Edit `filename` via double-click or context menu. """
        xpath = "//a[(@path='/%s')]" % filename
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        chain = ActionChains(self.browser)
        for i in range(10):
            try:
                if dclick:
                    chain.double_click(element).perform()
                else:
                    chain.context_click(element).perform()
                    self('file_edit').click()
            except StaleElementReferenceException:
                logging.warning('edit_file: StaleElementReferenceException')
                element = WebDriverWait(self.browser, 1).until(
                    lambda browser: browser.find_element_by_xpath(xpath))
                chain = ActionChains(self.browser)
            else:
                break

    def get_text_area(self):
        code_input_element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_css_selector('textarea'))
        # FIXME: absolute delay for editor to get ready.
        #        Problem is Firefox sometimes sends arrow key to scrollbar.
        #        Sadly this didn't completely fix the issue.
        time.sleep(1)
        return code_input_element

    def save_document(self, overwrite=False, check=True, cancel=False):
        #use 'save' button to save code
        self('editor_save_button').click()
        if overwrite:
            self('editor_overwrite_button').click()
        elif cancel:
            self('editor_cancel_button').click()
        if check:
            NotifierPage.wait(self)

    def add_text_to_file(self, text):
        """ Add the given text to the current file.  """
        # Switch to editor textarea
        code_input_element = self.get_text_area()

        # Type in the code.
        code_input_element.send_keys(text)
        return code_input_element
Esempio n. 23
0
class ConnectionsPage(DialogPage):
    """ Connection editor page. """

    source_component = InputElement((By.ID, 'src_cmp_list'))
    target_component = InputElement((By.ID, 'dst_cmp_list'))

    source_variable = InputElement((By.ID, 'src_var_list'))
    target_variable = InputElement((By.ID, 'dst_var_list'))

    connect_button = ButtonElement((By.ID, 'connect'))

    def set_source_component(self, comp_name):
        """ Set the source component. """
        self.source_component = comp_name + '\n'

    def set_target_component(self, comp_name):
        """ Set the target component. """
        self.target_component = comp_name + '\n'

    def connect(self):
        """ Connect the selected variables. """
        self('connect_button').click()

    def connect_vars(self, src, dst):
        """ Connect `src` to `dst`. """
        self.source_variable = src + '\n'
        self.target_variable = dst + '\n'
        self('connect_button').click()

    def check_variable_figures(self):
        """ Return number of variable figure elements, assume zero. """
        self.browser.implicitly_wait(1)
        try:
            figs = self.browser.find_elements_by_class_name('VariableFigureHeader')
            count = len(figs)
        finally:
            self.browser.implicitly_wait(TMO)
        return count

    def get_variable_figures(self):
        """ Return variable figure elements. """
        return self.browser.find_elements_by_class_name('VariableFigureHeader')

    def get_variable_names(self):
        """ Return names of variables. """
        # shameful copy/paste from workspace.get_dataflow_component_names()
        names = []

        # Assume there should be at least 1, wait for number to not change.
        n_found = 0
        for retry in range(10):
            variable_figure_headers = \
                self.browser.find_elements_by_class_name('VariableFigureHeader')
            if variable_figure_headers:
                n_headers = len(variable_figure_headers)
                if n_found:
                    if n_headers == n_found:
                        break
                n_found = n_headers
        else:
            logging.error('get_variable_names: n_found %s', n_found)
            return names

        for i in range(len(variable_figure_headers)):
            for retry in range(10):  # This has had issues...
                try:
                    names.append(self.browser.find_elements_by_class_name('VariableFigureHeader')[i].text)
                except StaleElementReferenceException:
                    logging.warning('get_variable_names:'
                                    ' StaleElementReferenceException')
                except IndexError:
                    logging.warning('get_variable_names:'
                                    ' IndexError for i=%s, headers=%s',
                                    i, len(variable_figure_headers))
                else:
                    break

        if len(names) != len(variable_figure_headers):
            logging.error('get_variable_names:'
                          ' expecting %d names, got %s',
                          len(variable_figure_headers), names)
        return names
Esempio n. 24
0
class WorkspacePage(BasePageObject):

    title_prefix = 'OpenMDAO:'

    # Top.
    project_menu = ButtonElement((By.ID, 'project-menu'))
    commit_button = ButtonElement((By.ID, 'project-commit'))
    revert_button = ButtonElement((By.ID, 'project-revert'))
    run_button = ButtonElement((By.ID, 'project-run'))
    reload_button = ButtonElement((By.ID, 'project-reload'))
    close_button = ButtonElement((By.ID, 'project-close'))
    exit_button = ButtonElement((By.ID, 'project-exit'))

    view_menu = ButtonElement((By.ID, 'view-menu'))
    objects_button = ButtonElement((By.ID, 'view-components'))
    console_button = ButtonElement((By.ID, 'view-console'))
    dataflow_button = ButtonElement((By.ID, 'view-dataflow'))
    files_button = ButtonElement((By.ID, 'view-files'))
    library_button = ButtonElement((By.ID, 'view-library'))
    properties_button = ButtonElement((By.ID, 'view-properties'))
    workflow_button = ButtonElement((By.ID, 'view-workflow'))
    refresh_button = ButtonElement((By.ID, 'view-refresh'))

    tools_menu = ButtonElement((By.ID, 'tools-menu'))
    editor_button = ButtonElement((By.ID, 'tools-editor'))
    plotter_button = ButtonElement((By.ID, 'tools-plotter'))
    drawing_button = ButtonElement((By.ID, 'tools-drawing'))
    log_button = ButtonElement((By.ID, 'tools-log'))

    help_menu = ButtonElement((By.ID, 'help-menu'))
    doc_button = ButtonElement((By.ID, 'help-doc'))

    about_button = ButtonElement((By.ID, 'about-item'))

    # Left side.
    objects_tab = ButtonElement((By.ID, 'otree_tab'))
    files_tab = ButtonElement((By.ID, 'ftree_tab'))

    # Object context menu.
    obj_properties = ButtonElement((By.XPATH, "//a[(@rel='properties')]"))
    obj_dataflow = ButtonElement((By.XPATH, "//a[(@rel='show_dataflow')]"))
    obj_workflow = ButtonElement((By.XPATH, "//a[(@rel='show_workflow')]"))
    obj_run = ButtonElement((By.XPATH, "//a[(@rel='run')]"))
    obj_toggle = ButtonElement((By.XPATH, "//a[(@rel='toggle')]"))
    obj_remove = ButtonElement((By.XPATH, "//a[(@rel='remove')]"))

    # File menu
    file_menu = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/a'))
    newfile_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/ul/li[1]/a'))
    newfolder_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/ul/li[2]/a'))
    add_button = ButtonElement(
        (By.XPATH, '/html/body/div/div/div/nav2/ul/li/ul/li[3]/a'))

    # File context menu.
    file_create = ButtonElement((By.XPATH, "//a[(@rel='createFile')]"))
    file_add = ButtonElement((By.XPATH, "//a[(@rel='addFile')]"))
    file_folder = ButtonElement((By.XPATH, "//a[(@rel='createFolder')]"))
    #    file_rename = ButtonElement((By.XPATH, "//a[(@rel='renameFile')]"))
    #    file_view   = ButtonElement((By.XPATH, "//a[(@rel='viewFile')]"))
    file_edit = ButtonElement((By.XPATH, "//a[(@rel='editFile')]"))
    file_import = ButtonElement((By.XPATH, "//a[(@rel='importFile')]"))
    file_exec = ButtonElement((By.XPATH, "//a[(@rel='execFile')]"))
    file_delete = ButtonElement((By.XPATH, "//a[(@rel='deleteFile')]"))
    file_toggle = ButtonElement((By.XPATH, "//a[(@rel='toggle')]"))

    file_chooser = InputElement((By.ID, 'filechooser'))

    # Center.
    dataflow_tab = ButtonElement((By.ID, 'dataflow_tab'))
    workflow_tab = ButtonElement((By.ID, 'workflow_tab'))

    # Right side.
    properties_tab = ButtonElement((By.ID, 'properties_tab'))
    props_header = TextElement((By.XPATH, "//div[@id='properties_pane']/h3"))
    props_inputs = GridElement(
        (By.XPATH, "//div[@id='properties_pane']/div[1]"))
    props_outputs = GridElement(
        (By.XPATH, "//div[@id='properties_pane']/div[2]"))

    library_tab = ButtonElement((By.ID, 'library_tab'))
    library_search = InputElement((By.ID, 'objtt-select'))
    library_clear = ButtonElement((By.ID, 'objtt-clear'))

    # Bottom.
    history = TextElement((By.ID, 'history'))
    command = InputElement((By.ID, 'command'))
    submit = ButtonElement((By.ID, 'command-button'))

    def __init__(self, browser, port):
        super(WorkspacePage, self).__init__(browser, port)

        self.locators = {}
        self.locators["objects"] = (By.XPATH,
                                    "//div[@id='otree_pane']//li[@path]")
        self.locators["files"] = (
            By.XPATH, "//div[@id='ftree_pane']//a[@class='file ui-draggable']")

        # Wait for bulk of page to load.
        WebDriverWait(
            self.browser,
            TMO).until(lambda browser: len(self.get_dataflow_figures()) > 0)

        # Now wait for all WebSockets open.
        browser.execute_script('openmdao.Util.webSocketsReady(2);')
        expected = 'WebSockets open'
        try:
            msg = NotifierPage.wait(self)
        except TimeoutException:  # Typically no exception text is provided.
            raise TimeoutException('Timed-out waiting for web sockets')
        while msg != expected:
            # During 'automatic' reloads we can see 'WebSockets closed'
            logging.warning('Acknowledged %r while waiting for %r', msg,
                            expected)
            time.sleep(1)
            try:
                msg = NotifierPage.wait(self)
            except TimeoutException:
                raise TimeoutException('Timed-out waiting for web sockets')

    def find_library_button(self, name, delay=0):
        path = "//table[(@id='objtypetable')]//td[text()='%s']" % name
        for retry in range(5):
            try:
                element = WebDriverWait(self.browser, TMO).until(
                    lambda browser: browser.find_element(By.XPATH, path))
            except TimeoutException as err:
                logging.warning(str(err))
            else:
                break
        else:
            raise err
        if delay:
            time.sleep(delay)
        return element

    def find_object_button(self, name, delay=0):
        path = "//div[@id='otree_pane']//li[(@path='%s')]//a" % name
        for retry in range(5):
            try:
                element = WebDriverWait(self.browser, TMO).until(
                    lambda browser: browser.find_element(By.XPATH, path))
            except TimeoutException as err:
                logging.warning(str(err))
            else:
                break
        else:
            raise err
        if delay:
            time.sleep(delay)
        return element

    def run(self, timeout=TMO):
        """ Run current component. """
        self('project_menu').click()
        self('run_button').click()
        NotifierPage.wait(self, timeout)

    def do_command(self, cmd, timeout=TMO, ack=True):
        """ Execute a command. """
        self.command = cmd
        self('submit').click()
        if ack:
            NotifierPage.wait(self, timeout, base_id='command')

    def close_workspace(self, commit=False):
        """ Close the workspace page. Returns :class:`ProjectsListPage`. """
        if commit:
            self.commit_project()
        self.browser.execute_script('openmdao.Util.closeWebSockets();')
        NotifierPage.wait(self)
        self('project_menu').click()
        self('close_button').click()

        from project import ProjectsListPage
        return ProjectsListPage.verify(self.browser, self.port)

    def attempt_to_close_workspace(self, expectDialog, confirm):
        """ Close the workspace page. Returns :class:`ProjectsListPage`. """
        self('project_menu').click()
        self('close_button').click()

        #if you expect the "close without saving?" dialog
        if expectDialog:
            dialog = ConfirmationPage(self)
            if confirm:  #close without saving
                self.browser.execute_script('openmdao.Util.closeWebSockets();')
                NotifierPage.wait(self)
                dialog.click_ok()
                from project import ProjectsListPage
                return ProjectsListPage.verify(self.browser, self.port)
            else:  #return to the project, intact.
                dialog.click_cancel()
        else:  #no unsaved changes
            from project import ProjectsListPage
            return ProjectsListPage.verify(self.browser, self.port)

    def open_editor(self):
        """ Open code editor.  Returns :class:`EditorPage`. """
        self('tools_menu').click()
        self('editor_button').click()
        self.browser.switch_to_window('Code Editor')
        return EditorPage.verify(self.browser, self.port)

    def get_files(self):
        """ Return names in the file tree. """
        WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element(By.ID, 'ftree_pane'))
        # FIXME: absolute delay for tree population.
        time.sleep(1)
        file_items = self.browser.find_elements(*self.locators["files"])
        file_names = []
        for i in range(len(file_items)):
            for retry in range(10):  # This has had issues...
                try:
                    file_names.append(
                        self.browser.find_elements(
                            *self.locators["files"])[i].text.strip())
                except StaleElementReferenceException:
                    logging.warning(
                        'get_files: StaleElementReferenceException')
                else:
                    break
        return file_names

    def add_file(self, file_path):
        """ Read in `file_path` """
        if file_path.endswith('.pyc'):
            file_path = file_path[:-1]

        self('files_tab').click()
        self('file_menu').click()
        self('add_button').click()

        self.file_chooser = file_path
        time.sleep(0.5)

    def new_file_dialog(self):
        """ bring up the new file dialog """
        self('files_tab').click()
        self('file_menu').click()
        self('newfile_button').click()
        return ValuePrompt(self.browser, self.port)

    def new_file(self, filename):
        """ Make a new empty file `filename`. """
        page = self.new_file_dialog()
        page.set_value(filename)
        NotifierPage.wait(self)  # Wait for creation to complete.

    def find_file(self, filename, tmo=TMO):
        """ Return elemnt corresponding to `filename`. """
        xpath = "//a[(@path='/%s')]" % filename
        return WebDriverWait(
            self.browser,
            tmo).until(lambda browser: browser.find_element_by_xpath(xpath))

    def edit_file(self, filename, dclick=True):
        """ Edit `filename` via double-click or context menu. """
        self('files_tab').click()
        element = self.find_file(filename)
        chain = ActionChains(self.browser)
        if dclick:  # This has had issues...
            for i in range(10):
                try:
                    chain.double_click(element).perform()
                except StaleElementReferenceException:
                    logging.warning(
                        'edit_file: StaleElementReferenceException')
                    element = self.find_file(filename, 1)
                    chain = ActionChains(self.browser)
                else:
                    break
        else:
            chain.context_click(element).perform()
            self('file_edit').click()
        self.browser.switch_to_window('Code Editor')
        return EditorPage.verify(self.browser, self.port)

    def expand_folder(self, filename):
        """ Expands `filename`. """
        self('files_tab').click()
        xpath = "//div[@id='ftree_pane']//a[(@path='/%s')]/../ins" % filename
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(1)  # Wait for cute animation.

    def toggle_files(self, filename):
        """ Toggle files display, using context menu of `filename`. """
        self('files_tab').click()
        time.sleep(0.5)
        element = self.find_file(filename)
        chain = ActionChains(self.browser)
        chain.context_click(element).perform()
        time.sleep(0.5)
        self('file_toggle').click()
        time.sleep(0.5)

    def commit_project(self, comment='no comment'):
        """ Commit current project. """
        self('project_menu').click()
        self('commit_button').click()
        page = ValuePrompt(self.browser, self.port)
        page.set_value(comment)
        NotifierPage.wait(self)

    def reload_project(self):
        """ Reload current project. """
        self('project_menu').click()
        self('reload_button').click()
        WorkspacePage.verify(self.browser, self.port)

    def get_objects_attribute(self, attribute, visible=False):
        """ Return list of `attribute` values for all objects. """
        WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element(By.ID, 'otree_pane'))
        object_elements = self.browser.find_elements(*self.locators["objects"])
        values = []
        for element in object_elements:
            if not visible or element.is_displayed():
                values.append(element.get_attribute(attribute))
        return values

    def select_object(self, component_name):
        """ Select `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()

    def expand_object(self, component_name):
        """ Expands `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//ins" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(1)  # Wait for cute animation.

    def show_dataflow(self, component_name):
        """ Show dataflow of `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(0.5)
        # Try to recover from context menu not getting displayed.
        for retry in range(3):
            chain = ActionChains(self.browser)
            chain.context_click(element).perform()
            try:
                self('obj_dataflow').click()
                break
            except TimeoutException:
                if retry >= 2:
                    raise

    def show_workflow(self, component_name):
        """ Show workflow of `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree_pane']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()
        time.sleep(0.5)
        # Try to recover from context menu not getting displayed.
        for retry in range(3):
            chain = ActionChains(self.browser)
            chain.context_click(element).perform()
            try:
                self('obj_workflow').click()
                break
            except TimeoutException:
                if retry >= 2:
                    raise

    def show_properties(self):
        """ Display properties. """
        # This has had some odd failures where the tab is highlighted as if
        # hovering over it, yet the Library tab is still the selected one.
        for retry in range(5):
            try:
                self('properties_tab').click()
                WebDriverWait(
                    self.browser,
                    1).until(lambda browser: self('props_header').is_visible)
            except TimeoutException:
                if retry:
                    logging.warning('TimeoutException in show_properties')
            else:
                break
        else:
            raise RuntimeError('Too many TimeoutExceptions')

    def show_library(self):
        """ Display library. """
        # For some reason the first try never works, so the wait is set
        # low and we expect to retry at least once.
        for retry in range(5):
            try:
                self('library_tab').click()
                WebDriverWait(
                    self.browser,
                    1).until(lambda browser: self('library_search').is_visible)
            except TimeoutException:
                if retry:
                    logging.warning('TimeoutException in show_library')
            else:
                break
        else:
            raise RuntimeError('Too many TimeoutExceptions')

    def set_library_filter(self, filter):
        """ Set the search filter text. """
        for retry in range(10):  # This has had issues...
            try:
                self.library_search = filter + '\n'
            except StaleElementReferenceException:
                logging.warning('set_library_filter:'
                                ' StaleElementReferenceException')
            else:
                break
        time.sleep(0.5)  # Wait for display update.

    def clear_library_filter(self):
        """ Clear the search filter via the 'X' button. """
        self('library_clear').click()
        time.sleep(0.5)  # Wait for display update.

    def get_object_types(self):
        """ Return displayed object types. """
        xpath = "//table[(@id='objtypetable')]//td"
        return [
            element.text
            for element in self.browser.find_elements(By.XPATH, xpath)
        ]

    def get_library_searches(self):
        """ Return stored library search terms. """
        self.library_search = 'searches'
        menu = self.browser.find_element(By.CLASS_NAME, 'ui-autocomplete')
        items = menu.find_elements(By.CLASS_NAME, 'ui-menu-item')
        searches = [item.text for item in items]
        self.clear_library_filter()
        return searches

    def get_library_item(self, item_name):
        """ Return element for library item `item_name`. """
        xpath = "//table[(@id='objtypetable')]//td[(@modpath='%s')]" % item_name
        library_item = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        WebDriverWait(self.browser,
                      TMO).until(lambda browser: library_item.is_displayed())
        return library_item

    def add_library_item_to_dataflow(self,
                                     item_name,
                                     instance_name,
                                     check=True,
                                     offset=None,
                                     prefix=None):
        """ Add component `item_name`, with name `instance_name`. """
        library_item = self.get_library_item(item_name)

        target = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(
                "//*[@id='-dataflow']"))

        offset = offset or (90, 90)
        chain = ActionChains(self.browser)
        if False:
            chain.drag_and_drop(library_item, target)
        else:
            chain.click_and_hold(library_item)
            chain.move_to_element_with_offset(target, offset[0], offset[1])
            chain.release(None)
        chain.perform()

        page = ValuePrompt(self.browser, self.port)
        page.set_value(instance_name)
        # Check that the prompt is gone so we can distinguish a prompt problem
        # from a dataflow update problem.
        time.sleep(0.25)
        self.browser.implicitly_wait(1)  # We don't expect to find anything.
        try:
            eq(len(self.browser.find_elements(*page('prompt')._locator)), 0)
        finally:
            self.browser.implicitly_wait(TMO)

        retval = None
        if check:  # Check that it's been added.
            retval = WebDriverWait(
                self.browser,
                TMO).until(lambda browser: self.get_dataflow_figure(
                    instance_name, prefix))
        return retval

    def get_dataflow_figures(self):
        """ Return dataflow figure elements. """
        return find_dataflow_figures(self)

    def get_dataflow_figure(self, name, prefix=None, retries=5):
        """ Return :class:`DataflowFigure` for `name`. """
        return find_dataflow_figure(self, name, prefix, retries)

    def get_dataflow_component_names(self):
        """ Return names of dataflow components. """
        return find_dataflow_component_names(self)

    def connect(self, src, dst):
        """ Return :class:`ConnectionsPage` for connecting `src` to `dst`. """
        chain = ActionChains(self.browser)
        chain.click_and_hold(src.output_port)
        # Using root rather than input_port since for some reason
        # even using a negative Y offset can select the parent's input.
        chain.move_to_element(dst.input_port)
        chain.release(None)
        chain.perform()
        parent, dot, srcname = src.pathname.rpartition('.')
        parent, dot, dstname = dst.pathname.rpartition('.')
        editor_id = 'ConnectionsFrame-%s' % (parent)
        editor_id = editor_id.replace('.', '-')
        return ConnectionsPage(self.browser, self.port, (By.ID, editor_id))

    def replace(self, name, classname, confirm=True):
        """ Replace `name` with an instance of `classname`. """
        library_item = self.get_library_item(classname)
        target = self.get_dataflow_figure(name).root

        chain = ActionChains(self.browser)
        chain.click_and_hold(library_item)
        chain.move_to_element_with_offset(target, 125, 30)
        chain.release(None)
        chain.perform()

        dialog = ConfirmationPage(self)
        if confirm:
            dialog.click_ok()
        else:
            dialog.click_cancel()

    def add_object_to_workflow(self, obj_path, target_name):
        """ Add `obj_path` object to `target_name` in workflow. """
        for retry in range(3):
            try:
                obj = self.find_object_button(obj_path)
                target = self.get_workflow_figure(target_name)
                chain = ActionChains(self.browser)
                chain.move_to_element(obj)
                chain.click_and_hold(obj)
                chain.move_to_element(target.root)
                chain.move_by_offset(2, 1)
                chain.release(None)
                chain.perform()
            except StaleElementReferenceException:
                if retry < 2:
                    logging.warning('add_object_to_workflow:'
                                    ' StaleElementReferenceException')
                else:
                    raise
            else:
                break

    def get_workflow_figures(self):
        """ Return workflow figure elements. """
        return find_workflow_figures(self)

    def get_workflow_component_figures(self):
        """ Return workflow component figure elements. """
        return find_workflow_component_figures(self)

    def get_workflow_figure(self, name, prefix=None, retries=5):
        """ Return :class:`WorkflowFigure` for `name`. """
        return find_workflow_figure(self, name, prefix, retries)

    def show_log(self):
        """ Open log viewer.  Returns :class:`LogViewer`. """
        self('tools_menu').click()
        self('log_button').click()
        return LogViewer.verify(self.browser, self.port)

    def hide_left(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-west-open')
        toggler.click()

    def show_left(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-west-closed')
        toggler.click()

    def hide_right(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-east-open')
        toggler.click()

    def show_right(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-east-closed')
        toggler.click()

    def hide_console(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-south-open')
        toggler.click()

    def show_console(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-south-closed')
        toggler.click()
Esempio n. 25
0
class ProjectsPage(BasePageObject):
    """ Displays list of projects. """

    url = '/projects'
    title_prefix = 'Projects'

    search_input = InputElement(
        (By.XPATH, "//div[@id='project_table_filter']/label/input"))
    import_button = ButtonElement((By.LINK_TEXT, 'Import Project'))
    new_button = ButtonElement((By.LINK_TEXT, 'New Project'))
    logout_button = ButtonElement((By.LINK_TEXT, 'Exit'))

    def new_project(self):
        """ Clicks the 'new' button. Returns :class:`NewDialog`. """
        self('new_button').click()
        return NewDialog(self.browser, self.port, (By.ID, "newProjectModal"))

    def import_project(self):
        """ Clicks the 'import' button. Returns :class:`ImportDialog`. """
        self('import_button').click()
        return ImportDialog(self.browser, self.port,
                            (By.ID, "importProjectModal"))

    def logout(self):
        """
        Clicks the 'logout' button. Returns :class:`LoginPage`.

        ..warning::

            Since login isn't done anymore, this actually results in
            exiting the server.

        """
        self('logout_button').click()
        from login import LoginPage
        return LoginPage.verify(self.browser, self.port)

    def contains(self, project_name):
        """ Returns True if `project_name` is in the list of projects. """
        elements = self.browser.find_elements(By.TAG_NAME, 'tr')
        if len(elements) == 0:
            return False

        self.search_input = project_name
        return len(self.browser.find_elements_by_link_text(project_name)) > 0

    def open_project(self, project_name):
        """ Clicks the named link. Returns :class:`WorkspacePage`. """
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element.click()
        from workspace import WorkspacePage
        return WorkspacePage.verify(self.browser, self.port)

    def edit_project(self, project_name):
        """ Clicks the 'edit' button. Returns :class:`EditDialog`. """
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element = element.find_element_by_xpath('../../td[6]/a')
        element.click()

        edit_dialog = EditDialog(self.browser, self.port,
                                 (By.ID, "editProjectModal"))
        return edit_dialog

    def export_project(self, project_name):
        """ Clicks the 'export' button. Returns :class:`ExportDialog`. """
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element = element.find_element_by_xpath('../../td[6]/form[2]/button')
        element.click()

    def delete_project(self, project_name):
        self.search_input = project_name
        element = WebDriverWait(self.browser, TMO).until(
            lambda browser: browser.find_element_by_link_text(project_name))
        element = element.find_element_by_xpath('../../td[6]/form[1]/button')
        element.click()

        delete_dialog = DeleteDialog(self.browser, self.port,
                                     (By.XPATH, "/html/body/div[2]"))

        time.sleep(5)

        delete_dialog.submit()

    def delete_all_test_projects(self, verbose=False):
        """ Removes all projects with 'test project' in the name. """
        elements = self.browser.find_elements_by_partial_link_text(
            'testing project')
        while len(elements) > 0:
            for i in range(len(elements)):
                element = WebDriverWait(self.browser, TMO).until(
                    lambda browser: browser.find_element_by_partial_link_text(
                        'testing project'))

                project_name = element.text
                self.delete_project(project_name)
                if verbose:
                    print >> sys.stderr, 'Deleted', project_name
            # there may be more that were previously hidden due to the row limit
            elements = self.browser.find_elements_by_partial_link_text(
                'testing project')
Esempio n. 26
0
 def select_private_radio(self):
     locator = CreateRepositoryPageLocator.select_private_radio
     return InputElement(driver=self.driver, locator=locator)
Esempio n. 27
0
class ComponentPage(DialogPage):
    class Variable(object):
        def __init__(self, row, headers):
            self._cells = row.cells

            def getter(cls, index=0):
                return cls._cells[index]

            def setter(cls, value, index=0):
                if not cls._cells[index].editable:
                    raise AttributeError("can't set attribute")
                else:
                    cls._cells[index].value = value

            for index in range(len(headers)):
                if headers[index].value != "":
                    setattr(
                        self.__class__, headers[index].value.lower(),
                        property(partial(getter, index=index),
                                 partial(setter, index=index)))

    """ Component editor page. """

    Version = type('Enum', (), {"OLD": 1, "NEW": 2})
    As = type('Enum', (), {"GRID": 0, "ROW": 1, "VARIABLE": 2})
    SortOrder = type('Enum', (), {"ASCENDING": 0, "DESCENDING": 1})

    inputs_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Inputs']"))
    slots_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Slots']"))
    outputs_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Outputs']"))
    events_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Events']"))

    inputs = GridElement((By.ID, 'Inputs_props'))
    outputs = GridElement((By.ID, 'Outputs_props'))

    inputs_filter = InputElement((By.ID, 'Inputs_variableFilter'))
    inputs_clear = ButtonElement((By.ID, 'Inputs_clear'))

    outputs_filter = InputElement((By.ID, 'Outputs_variableFilter'))
    outputs_clear = ButtonElement((By.ID, 'Outputs_clear'))

    def __init__(self, browser, port, locator, version=Version.OLD):
        super(ComponentPage, self).__init__(browser, port, locator)
        # It takes a while for the full load to complete.
        NotifierPage.wait(self)
        self.version = version
        self._sort_order = {"inputs": 0, "outputs": 0}
        self._column_picker = None

    def get_tab_labels(self):
        """ Return a list of the tab labels. """
        elements = self.root.find_elements_by_class_name('ui-tabs-anchor')
        labels = [element.text for element in elements]
        return labels

    def set_input(self, name, value):
        """ Set input `name` to `value`. """
        self('inputs_tab').click()
        grid = self.inputs
        found = []
        for row in grid.rows:
            if row[1] == name:
                row[2] = value
                return
            found.append(row[1])
        raise RuntimeError('%r not found in inputs %s' % (name, found))

    def filter_inputs(self, filter_text):
        """ Filter out input variables from grid using `filter_text`. """
        self.inputs_filter = filter_text

    def filter_outputs(self, filter_text):
        """ Filter out output variables from grid using `filter_text`. """
        self.outputs_filter = filter_text

    def clear_inputs_filter(self):
        """ Clear input variable filter. """
        self('inputs_clear').click()

    def clear_outputs_filter(self):
        """ Clear output variable filter. """
        self('outputs_clear').click()

    def _sort_column(self, grid, column_name, sort_order, tab):
        """ Sorts the variables in column `column_name`"""
        header = [
            header for header in grid.headers if header.value == column_name
        ]
        if (not header):
            raise Exception("Grid has no column named %s" % column_name)

        header = header[0]
        if (sort_order == self.SortOrder.ASCENDING):
            while ((self._sort_order[tab] % 2) == 0):
                self._sort_order[tab] = self._sort_order[tab] + 1
                header.click()
        else:
            while ((self._sort_order[tab] % 2) != 0):
                self._sort_order[tab] = self._sort_order[tab] + 1
                header.click()

    def sort_inputs_column(self, column_name, sort_order=SortOrder.ASCENDING):
        """ Sort `column_name` in inputs grid in `sort_order` """
        self("inputs_tab").click()
        self._sort_column(self.inputs, column_name, sort_order, "inputs")

    def sort_outputs_column(self, column_name, sort_order=SortOrder.ASCENDING):
        """ Sort `column_name` in outputs grid in `sort_order` """
        self("outputs_tab").click()
        self._sort_column(self.outputs, column_name, sort_order, "outputs")

    def get_events(self):
        """ Return events grid. """
        self('events_tab').click()
        return self.events

    def show_inputs(self):
        """switch to inputs tab"""
        self('inputs_tab').click()

    def show_outputs(self):
        """switch to outputs tab"""
        self('outputs_tab').click()

    def show_slots(self):
        """switch to slots tab"""
        self('slots_tab').click()

    def toggle_column_visibility(self, column_name):
        self._toggle_column_visibility(self._active_grid, column_name)

    @property
    def _active_grid(self):
        if self.inputs.displayed:
            return self.inputs

        elif self.outputs.displayed:
            return self.outputs

    def _toggle_column_visibility(self, grid, column_name):
        if not self._column_picker:
            self._column_picker = self._get_column_picker(grid)

        elif not self._column_picker.displayed:
            self._column_picker = self._get_column_picker(grid)

        self._column_picker.get_option(column_name).click()

    def _get_column_picker(self, grid):
        grid.headers[0].context_click()
        column_pickers = [
            GridColumnPicker(self.browser, element) for element in
            self.browser.find_elements(By.CLASS_NAME, "slick-columnpicker")
        ]
        for column_picker in column_pickers:
            if column_picker.displayed:
                return column_picker

    def get_inputs(self, return_type=None):
        """ Return inputs grid. """
        self('inputs_tab').click()
        return self._get_variables(self.inputs, return_type)

    def get_outputs(self, return_type=None):
        """ Return outputs grid. """
        self('outputs_tab').click()
        return self._get_variables(self.outputs, return_type)

    def get_input(self, name, return_type=None):
        """ Return first input variable with `name`. """
        self('inputs_tab').click()
        return self._get_variable(name, self.inputs, return_type)

    def get_output(self, name, return_type=None):
        """ Return first output variable with `name`. """
        self('outputs_tab').click()
        return self._get_variable(name, self.outputs, return_type)

    def _get_variables(self, grid, return_type):
        if (return_type == self.As.GRID or self.version == self.Version.OLD):
            return grid

        headers = grid.headers
        rows = grid.rows

        return [self.Variable(row, headers) for row in rows]

    def _get_variable(self, name, grid, return_type):
        found = []
        for row in grid.rows:
            if row[1] == name:
                if (return_type == self.As.ROW
                        or self.version == self.Version.OLD):
                    return row
                else:
                    return self.Variable(row, grid.headers)
            found.append(row[1])
        raise RuntimeError('%r not found in inputs %s' % (name, found))
Esempio n. 28
0
class ConnectionsPage(DialogPage):
    """ Connection editor page. """

    source_component = InputElement((By.ID, 'src_cmp_input'))
    target_component = InputElement((By.ID, 'tgt_cmp_input'))

    source_variable = InputElement((By.ID, 'src_var_input'))
    target_variable = InputElement((By.ID, 'tgt_var_input'))

    expression_button = ButtonElement((By.ID, 'expression'))
    connect_button    = ButtonElement((By.ID, 'connect'))

    # Context menu.
    show_all_button       = ButtonElement((By.XPATH, "./div/ul/li[text()='Show All Variables']"))
    show_connected_button = ButtonElement((By.XPATH, "./div/ul/li[text()='Show Connected Variables Only']"))

    def set_source_component(self, comp_name):
        """ Set the source component. """
        self.source_component = comp_name + '\n'

    def set_target_component(self, comp_name):
        """ Set the target component. """
        self.target_component = comp_name + '\n'

    def set_source_expression(self, expr):
        """ Set the source to expression. """
        self('expression_button').click()
        dialog = ValuePrompt(self.browser, self.port)
        dialog.set_value(expr)

    def connect(self):
        """ Connect the selected variables. """
        self('connect_button').click()

    def connect_vars(self, src, tgt):
        """ Connect `src` to `tgt`. """
        self.source_variable = src + '\n'
        self.target_variable = tgt + '\n'
        self('connect_button').click()

    @property
    def components_pane(self):
        """ Component selector pane. """
        children = self.root.find_elements_by_xpath('./div')
        return children[0]

    @property
    def connections_pane(self):
        """ Connections pane. """
        children = self.root.find_elements_by_xpath('./div')
        return children[1]

    @property
    def variables_pane(self):
        """ Variable selector pane. """
        children = self.root.find_elements_by_xpath('./div')
        return children[2]

    def show_all_variables(self):
        """ Show all variables. """
        chain = ActionChains(self.browser)
        chain.move_to_element_with_offset(self.connections_pane, 5, 5)
        chain.context_click(None)
        chain.perform()
        time.sleep(0.5)
        self('show_all_button').click()

    def show_connected_variables(self):
        """ Show only the connected variables. """
        chain = ActionChains(self.browser)
        chain.move_to_element_with_offset(self.connections_pane, 5, 5)
        chain.context_click(None)
        chain.perform()
        time.sleep(0.5)
        self('show_connected_button').click()

    def count_variable_figures(self):
        """ Return number of variable figure elements, assume zero. """
        self.browser.implicitly_wait(1)
        try:
            figs = self.browser.find_elements_by_class_name('variable-figure')
            count = len(figs)
        finally:
            self.browser.implicitly_wait(TMO)
        return count

    def count_variable_connections(self):
        """ Return number of variable figure elements, assume zero. """
        self.browser.implicitly_wait(1)
        try:
            figs = self.browser.find_elements_by_class_name('variable-connection')
            count = len(figs)
        finally:
            self.browser.implicitly_wait(TMO)
        return count

    def get_variable_figures(self):
        """ Return variable figure elements. """
        return self.browser.find_elements_by_class_name('variable-figure')

    def get_variable_names(self):
        """ Return names of variable figures. """
        # shameful copy/paste from workspace.get_dataflow_component_names()
        names = []

        # Assume there should be at least 1, wait for number to not change.
        n_found = 0
        for retry in range(10):
            variable_names = \
                self.browser.find_elements_by_class_name('variable-name')
            if variable_names:
                n_names = len(variable_names)
                if n_found:
                    if n_names == n_found:
                        break
                n_found = n_names
        else:
            logging.error('get_variable_names: n_found %s', n_found)
            return names

        for i in range(len(variable_names)):
            for retry in range(10):  # This has had issues...
                try:
                    names.append(self.browser.find_elements_by_class_name('variable-name')[i].text)
                except StaleElementReferenceException:
                    logging.warning('get_variable_names:'
                                    ' StaleElementReferenceException')
                except IndexError:
                    logging.warning('get_variable_names:'
                                    ' IndexError for i=%s, headers=%s',
                                    i, len(variable_names))
                else:
                    break

        if len(names) != len(variable_names):
            logging.error('get_variable_names:'
                          ' expecting %d names, got %s',
                          len(variable_names), names)
        return names

    def find_variable_name(self, name):
        """ Return the variable name elements containing the name `name`. """
        names = []

        # Assume there should be at least 1, wait for number to not change.
        n_found = 0
        for retry in range(10):
            variable_names = \
                self.browser.find_elements_by_class_name('variable-name')
            if variable_names:
                n_names = len(variable_names)
                if n_found:
                    if n_names == n_found:
                        break
                n_found = n_names
        else:
            logging.error('find_variable_name: n_found %s', n_found)
            return names

        for i in range(len(variable_names)):
            for retry in range(10):  # This has had issues...
                try:
                    elm = self.browser.find_elements_by_class_name('variable-name')[i]
                    if elm.text == name:
                        names.append(elm)
                except StaleElementReferenceException:
                    logging.warning('find_variable_name:'
                                    ' StaleElementReferenceException')
                except IndexError:
                    logging.warning('find_variable_name:'
                                    ' IndexError for i=%s, headers=%s',
                                    i, len(variable_names))
                else:
                    break

        return names
Esempio n. 29
0
 def username_input(self):
     locator = LoginPageLocator.username_input
     return InputElement(driver=self.driver, locator=locator)
Esempio n. 30
0
class WorkspacePage(BasePageObject):

    title_prefix = 'OpenMDAO:'

    # Top.
    project_menu = ButtonElement((By.ID, 'project-menu'))
    save_button = ButtonElement((By.ID, 'project-save'))
    run_button = ButtonElement((By.ID, 'project-run'))
    reload_button = ButtonElement((By.ID, 'project-reload'))
    close_button = ButtonElement((By.ID, 'project-close'))
    exit_button = ButtonElement((By.ID, 'project-exit'))

    view_menu = ButtonElement((By.ID, 'view-menu'))
    cmdline_button = ButtonElement((By.ID, 'view-cmdline'))
    console_button = ButtonElement((By.ID, 'view-console'))
    libraries_button = ButtonElement((By.ID, 'view-libraries'))
    objects_button = ButtonElement((By.ID, 'view-objects'))
    properties_button = ButtonElement((By.ID, 'view-properties'))
    workflow_button = ButtonElement((By.ID, 'view-workflow'))
    dataflow_button = ButtonElement((By.ID, 'view-dataflow'))
    refresh_button = ButtonElement((By.ID, 'view-refresh'))

    tools_menu = ButtonElement((By.ID, 'tools-menu'))
    editor_button = ButtonElement((By.ID, 'tools-editor'))
    plotter_button = ButtonElement((By.ID, 'tools-plotter'))
    addons_button = ButtonElement((By.ID, 'tools-addons'))

    help_menu = ButtonElement((By.ID, 'help-menu'))
    doc_button = ButtonElement((By.ID, 'help-doc'))

    about_button = ButtonElement((By.ID, 'about-item'))

    # Left side.
    objects_tab = ButtonElement((By.ID, 'otree_tab'))

    # Object context menu.
    obj_properties = ButtonElement((By.XPATH, "//a[(@rel='properties')]"))
    obj_dataflow = ButtonElement((By.XPATH, "//a[(@rel='show_dataflow')]"))
    obj_workflow = ButtonElement((By.XPATH, "//a[(@rel='show_workflow')]"))
    obj_run = ButtonElement((By.XPATH, "//a[(@rel='run')]"))
    obj_toggle = ButtonElement((By.XPATH, "//a[(@rel='toggle')]"))
    obj_remove = ButtonElement((By.XPATH, "//a[(@rel='remove')]"))

    # Center.
    dataflow_tab = ButtonElement((By.ID, 'dataflow_tab'))
    workflow_tab = ButtonElement((By.ID, 'workflow_tab'))
    code_tab = ButtonElement((By.ID, 'code_tab'))

    # Right side.
    properties_tab = ButtonElement((By.ID, 'properties_tab'))
    props_header = TextElement((By.XPATH, "//div[@id='propertieseditor']/h3"))
    props_inputs = GridElement(
        (By.XPATH, "//div[@id='propertieseditor']/div[1]"))
    props_outputs = GridElement(
        (By.XPATH, "//div[@id='propertieseditor']/div[2]"))

    libraries_tab = ButtonElement((By.ID, 'palette_tab'))
    libraries_searchbox = InputElement((By.ID, 'objtt-select'))

    # Bottom.
    history = TextElement((By.ID, 'history'))
    command = InputElement((By.ID, 'command'))
    submit = ButtonElement((By.ID, 'command-button'))

    def __init__(self, browser, port):
        super(WorkspacePage, self).__init__(browser, port)

        self.locators = {}
        self.locators["objects"] = (By.XPATH, "//div[@id='otree']//li[@path]")

        # Wait for bulk of page to load.
        WebDriverWait(
            self.browser, 2 *
            TMO).until(lambda browser: len(self.get_dataflow_figures()) > 0)
        # Now wait for WebSockets.
        # FIXME: absolute delay before polling sockets.
        time.sleep(2)
        browser.execute_script('openmdao.Util.webSocketsReady(2);')
        NotifierPage.wait(browser, port)

    def find_palette_button(self, name):
        path = "//table[(@id='objtypetable')]//td[text()='%s']" % name
        return ButtonElement((By.XPATH, path)).get(self)

    def run(self, timeout=TMO):
        """ Run current component. """
        self('project_menu').click()
        self('run_button').click()
        NotifierPage.wait(self.browser, self.port, timeout)

    def do_command(self, cmd, timeout=TMO):
        """ Execute a command. """
        self.command = cmd
        self('submit').click()
        NotifierPage.wait(self.browser, self.port, timeout)

    def close_workspace(self):
        """ Close the workspace page. Returns :class:`ProjectsListPage`. """
        self.browser.execute_script('openmdao.Util.closeWebSockets();')
        NotifierPage.wait(self.browser, self.port)
        self('project_menu').click()

        # Sometimes chromedriver hangs here, so we click in separate thread.
        # It's a known issue on the chromedriver site.
        closer = threading.Thread(target=self._closer)
        closer.daemon = True
        closer.start()
        closer.join(60)
        if closer.is_alive():
            abort(True)
            raise SkipTest("Can't close workspace, driver hung :-(")

        from project import ProjectsListPage
        return ProjectsListPage.verify(self.browser, self.port)

    def _closer(self):
        """ Clicks the close button. """
        self('close_button').click()

    def open_editor(self):
        """ Open code editor.  Returns :class:`EditorPage`. """
        self('tools_menu').click()
        self('editor_button').click()
        self.browser.switch_to_window('Code Editor')
        return EditorPage.verify(self.browser, self.port)

    def save_project(self):
        """ Save current project. """
        self('project_menu').click()
        self('save_button').click()
        NotifierPage.wait(self.browser, self.port)

    def get_objects_attribute(self, attribute):
        """ Return list of `attribute` values for all objects. """
        WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element(By.ID, 'otree'))
        object_elements = self.browser.find_elements(*self.locators["objects"])
        values = []
        for element in object_elements:
            values.append(element.get_attribute(attribute))
        return values

    def select_object(self, component_name):
        """ Select `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()

    def expand_object(self, component_name):
        """ Expands `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree']//li[(@path='%s')]//ins" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        element.click()

    def show_dataflow(self, component_name):
        """ Show dataflow of `component_name`. """
        self('objects_tab').click()
        xpath = "//div[@id='otree']//li[(@path='%s')]//a" % component_name
        element = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        chain = ActionChains(self.browser)
        chain.context_click(element).perform()
        self('obj_dataflow').click()

    @property
    def libraries_search(self):
        """ The contents of the libraries_search box. """
        return self('libraries_searchbox').value

    @libraries_search.setter
    def libraries_search(self, value):
        """ Set the value of the libraries_search box. """
        self.libraries_searchbox = value

    def add_library_item_to_dataflow(self, item_name, instance_name):
        """ Add component `item_name`, with name `instance_name`. """
        #xpath = "//div[(@id='palette')]//div[(@path='%s')]" % item_name
        xpath = "//table[(@id='objtypetable')]//td[(@modpath='%s')]" % item_name
        library_item = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(xpath))
        WebDriverWait(self.browser,
                      TMO).until(lambda browser: library_item.is_displayed())
        # FIXME: absolute delay to wait for 'slide' to complete.
        time.sleep(1)

        target = WebDriverWait(
            self.browser,
            TMO).until(lambda browser: browser.find_element_by_xpath(
                "//*[@id='-dataflow']"))

        chain = ActionChains(self.browser)
        if False:
            chain = chain.drag_and_drop(library_item, target)
        else:
            chain = chain.click_and_hold(library_item)
            chain = chain.move_to_element_with_offset(target, 100, 100)
            chain = chain.release(None)
        chain.perform()

        page = ValuePrompt(self.browser, self.port)
        page.set_value(instance_name)
        # Check that the prompt is gone so we can distinguish a prompt problem
        # from a dataflow update problem.
        time.sleep(0.25)
        eq(len(self.browser.find_elements(*page('prompt')._locator)), 0)
        WebDriverWait(self.browser,
                      TMO).until(lambda browser: instance_name in self.
                                 get_dataflow_component_names())

    def get_dataflow_figures(self):
        """ Return dataflow figure elements. """
        return self.browser.find_elements_by_class_name('DataflowFigure')

    def get_dataflow_figure(self, name, prefix=None, retries=5):
        """ Return :class:`DataflowFigure` for `name`. """
        for retry in range(retries):
            time.sleep(0.5)
            figures = self.browser.find_elements_by_class_name(
                'DataflowFigure')
            if not figures:
                continue
            fig_name = None
            for figure in figures:
                try:
                    header = figure.find_elements_by_class_name(
                        'DataflowFigureHeader')
                    if len(header) == 0:
                        # the outermost figure (globals) has no header or name
                        if name == '' and prefix is None:
                            fig = DataflowFigure(self.browser, self.port,
                                                 figure)
                            return fig
                        else:
                            continue
                    fig_name = figure.find_elements_by_class_name(
                        'DataflowFigureHeader')[0].text
                except StaleElementReferenceException:
                    logging.warning('get_dataflow_figure:'
                                    ' StaleElementReferenceException')
                else:
                    if fig_name == name:
                        fig = DataflowFigure(self.browser, self.port, figure)
                        if prefix is not None:
                            if prefix:
                                fig.pathname = '%s.%s' % (prefix, name)
                            else:
                                fig.pathname = name
                        return fig
        return None

    def get_dataflow_component_names(self):
        """ Return names of dataflow components. """
        names = []

        # Assume there should be at least 1, wait for number to not change.
        n_found = 0
        for retry in range(10):
            dataflow_component_headers = \
                self.browser.find_elements_by_class_name('DataflowFigureHeader')
            if dataflow_component_headers:
                n_headers = len(dataflow_component_headers)
                if n_found:
                    if n_headers == n_found:
                        break
                n_found = n_headers
        else:
            logging.error('get_dataflow_component_names: n_found %s', n_found)
            return names

        for i in range(len(dataflow_component_headers)):
            for retry in range(10):  # This has had issues...
                try:
                    names.append(
                        self.browser.find_elements_by_class_name(
                            'DataflowFigureHeader')[i].text)
                except StaleElementReferenceException:
                    logging.warning('get_dataflow_component_names:'
                                    ' StaleElementReferenceException')
                except IndexError:
                    logging.warning(
                        'get_dataflow_component_names:'
                        ' IndexError for i=%s, headers=%s', i,
                        len(dataflow_component_headers))
                else:
                    break

        if len(names) != len(dataflow_component_headers):
            logging.error(
                'get_dataflow_component_names:'
                ' expecting %d names, got %s', len(dataflow_component_headers),
                names)
        return names

    def connect(self, src, dst):
        """ Return :class:`ConnectionsPage` for connecting `src` to `dst`. """
        chain = ActionChains(self.browser)
        chain = chain.click_and_hold(src.output_port)
        # Using root rather than input_port since for some reason
        # even using a negative Y offset can select the parent's input.
        chain = chain.move_to_element(dst.root)
        chain = chain.release(None)
        chain.perform()
        parent, dot, srcname = src.pathname.rpartition('.')
        parent, dot, dstname = dst.pathname.rpartition('.')
        editor_id = 'ConnectionsFrame-%s' % (parent)
        editor_id = editor_id.replace('.', '-')
        return ConnectionsPage(self.browser, self.port, (By.ID, editor_id))

    def hide_left(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-west-open')
        toggler.click()

    def show_left(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-west-closed')
        toggler.click()

    def hide_right(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-east-open')
        toggler.click()

    def show_right(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-east-closed')
        toggler.click()

    def hide_console(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-south-open')
        toggler.click()

    def show_console(self):
        toggler = self.browser.find_element_by_css_selector(
            '.ui-layout-toggler-south-closed')
        toggler.click()