class ComponentPage(DialogPage): """ Component editor page. """ dataflow_tab = ButtonElement((By.XPATH, "dl/dt[text()='Dataflow']")) inputs_tab = ButtonElement((By.XPATH, "dl/dt[text()='Inputs']")) slots_tab = ButtonElement((By.XPATH, "dl/dt[text()='Slots']")) outputs_tab = ButtonElement((By.XPATH, "dl/dt[text()='Outputs']")) inputs = GridElement((By.ID, 'Inputs_props')) outputs = GridElement((By.ID, 'Outputs_props')) def get_inputs(self): """ Return inputs grid. """ self('inputs_tab').click() return self.inputs 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[0] == name: row[2] = value return found.append(row[0]) raise RuntimeError('%r not found in inputs %s' % (name, found)) def get_outputs(self): """ Return outputs grid. """ self('outputs_tab').click() return self.outputs
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 EditDialog(MetadataModal): """ Dialog for exporting a project """ submit_button = ButtonElement( (By.XPATH, "div[2]/form/div[@class='modal-footer']/div/input")) cancel_button = ButtonElement( (By.XPATH, "div[2]/form/div[@class='modal-footer']/div/button"))
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'))
class DialogPage(BasePageObject): """ Base for various dialog pages. """ dialog_title = TextElement((By.XPATH, '../div/span')) close_button = ButtonElement((By.XPATH, '../div/a/span')) dialog_resize = ButtonElement((By.XPATH, '../div[contains(@class, "ui-resizable-se")]')) def __init__(self, browser, port, locator): root = WebDriverWait(browser, TMO).until( lambda browser: browser.find_element(*locator)) super(DialogPage, self).__init__(browser, port, root) @property def is_visible(self): """ True if dialog is visible. """ return self('dialog_title').is_visible def close(self): """ Close dialog. """ # Ensure close button is on screen. width = self.browser.get_window_size()['width'] x = self('close_button').location['x'] shift = width - x if shift < 0: self.move(shift, 0) self('close_button').click() def move(self, delta_x, delta_y): """ Move dialog. """ chain = ActionChains(self.browser) chain.click_and_hold(self('dialog_title').element) chain.move_by_offset(int(delta_x), int(delta_y)) chain.release(None) chain.perform() time.sleep(0.5)
class WorkflowComponentFigure(BasePageObject): """ Represents elements within a workflow figure. """ # Context menu. edit_button = ButtonElement((By.XPATH, "./ul/li[text()='Edit']")) properties_button = ButtonElement((By.XPATH, "./ul/li[text()='Properties']")) run_button = ButtonElement((By.XPATH, "./ul/li[text()='Run']")) evaluate_button = ButtonElement((By.XPATH, "./ul/li[text()='Evaluate']")) remove_button = ButtonElement((By.XPATH, "./ul/li[text()='Remove from Workflow']")) @property def pathname(self): """ Pathname of this component. """ return self._pathname @pathname.setter def pathname(self, path): self._pathname = path @property def state(self): """ Exec state of this component. """ rect = self.root.find_element_by_css_selector('rect') style = Style(rect.get_attribute('style')) stroke = Color.from_string(style.stroke) #red stroke if(stroke == Color(255, 0, 0)): return 'INVALID' #green stroke if(stroke == Color(0, 255, 0)): return 'VALID' #blue stroke if(stroke == Color(0, 0, 255)): return 'RUNNING' return 'UNKNOWN' def evaluate(self): """ Evaluate this component. (only available for ImplicitComponent) """ rect = self.root.find_element_by_css_selector('rect') chain = ActionChains(self.browser) chain.move_to_element_with_offset(rect, 15, 15) chain.context_click(None) chain.perform() time.sleep(0.5) self('evaluate_button').click() def run(self): """ Run this component. """ rect = self.root.find_element_by_css_selector('rect') chain = ActionChains(self.browser) chain.move_to_element_with_offset(rect, 15, 15) chain.context_click(None) chain.perform() time.sleep(0.5) self('run_button').click()
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'))
class SlotFigure(BasePageObject): """ Represents a slot in an editor's Slots page. """ # Context menu. edit_button = ButtonElement((By.XPATH, "./ul/li[text()='Edit']")) remove_button = ButtonElement((By.XPATH, "./ul/li[text()='Remove']")) def __init__(self, browser, port, root): super(SlotFigure, self).__init__(browser, port, root) self._pathname = None @property def filled(self): """ property indicating if the slot is filled. """ element = self.root.find_element_by_xpath('.') classes = element.get_attribute('class') return ('filled' in classes) def editor_page(self, double_click=True): """ Return :class:`ComponentPage` for this component. """ for retry in range(3): try: chain = ActionChains(self.browser) if double_click: chain.double_click(self.root).perform() else: self._context_click('edit_button') chain.release(None).perform() except StaleElementReferenceException: if retry < 2: logging.warning('StaleElementReferenceException' ' in SlotFigure.editor_page()') else: raise else: break editor_id = 'ObjectFrame_%s' % self.pathname.replace('.', '-') return ComponentPage(self.browser, self.port, (By.ID, editor_id)) def edit(self, offset=15): """ Edit this component. """ self._context_click('edit_button', offset) editor_id = 'ObjectFrame_%s' % self.pathname.replace('.', '-') return ComponentPage(self.browser, self.port, (By.ID, editor_id)) def remove(self, offset=15): """ Remove this component. """ self._context_click('remove_button', offset) def _context_click(self, name, offset=15): """ Display context menu. """ chain = ActionChains(self.browser) chain.move_to_element_with_offset(self.root, offset, 15) chain.context_click(None) chain.perform() time.sleep(0.5) self(name).click()
class EditDialog(MetadataModal): """ Dialog for exporting a project """ submit_button = ButtonElement( (By.XPATH, "div[2]/form/div[@class='modal-footer']/div/input")) cancel_button = ButtonElement( (By.XPATH, "div[2]/form/div[@class='modal-footer']/div/button")) def __init__(self, browser, port, locator): super(EditDialog, self).__init__(browser, port, locator)
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()
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'))
class DeleteDialog(DialogPage): """ Dialog for deleting a project """ delete_button = ButtonElement((By.XPATH, "div[@class='modal-footer']/a[text()='OK']")) cancel_button = ButtonElement((By.XPATH, "div[@class='modal-footer']/a[text()='Cancel']")) def submit(self): """Clicks the 'delete' button""" self('delete_button').click() def cancel(self): """Clicks the 'cancel' button""" self('cancel_button').click()
class NewDialog(MetadataModal): """ Modal for creating a new project """ submit_button = ButtonElement((By.XPATH, "form/div[@class='modal-footer']/button[text()='New Project']")) cancel_button = ButtonElement((By.XPATH, "form/div[@class='modal-footer']/button[text()='Cancel']")) create_button = submit_button @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 LogViewer(DialogPage): """ Log message viewer. """ data = TextElement((By.ID, 'logdata')) # Context menu. pause_button = ButtonElement((By.XPATH, 'ul/li[1]')) popout_button = ButtonElement((By.XPATH, 'ul/li[2]')) filter_button = ButtonElement((By.XPATH, 'ul/li[3]')) copy_button = ButtonElement((By.XPATH, 'ul/li[4]')) clear_button = ButtonElement((By.XPATH, 'ul/li[6]')) def __init__(self, browser, port): super(LogViewer, self).__init__(browser, port, (By.ID, 'logframe')) time.sleep(1) # Wait for display to load-up. def clear(self): """ Clear display. """ self._context_click('clear_button') def filter(self): """ Return filtering dialog. """ self._context_click('filter_button') return FilterDialog.verify(self.browser, self.port) def pause(self): """ Pause/resume display. """ return self._context_click('pause_button') def popout(self): """ Pop-out display to separate window. """ return self._context_click('popout_button') def _context_click(self, name): """ Display context menu. """ chain = ActionChains(self.browser) # Just using center of self.root had an isolated 'overlap' failure. chain.move_to_element_with_offset(self('data').element, 2, 2) chain.context_click(None) chain.perform() time.sleep(0.5) # Wait for menu to display. text = self(name).text self(name).click() time.sleep(0.5) # Wait to pop-down. return text def get_messages(self): """ Return messages as a list. """ time.sleep(0.5) # Wait for update. return self.data.split('\n')
class ProjectsListPage(BasePageObject): """ Displays list of projects. """ url = '/projects' title_prefix = 'Projects' new_button = ButtonElement((By.LINK_TEXT, 'Start new project')) add_button = ButtonElement((By.LINK_TEXT, 'Add existing project')) logout_button = ButtonElement((By.LINK_TEXT, 'Exit')) def new_project(self): """ Clicks the 'new' button. Returns :class:`NewProjectPage`. """ self('new_button').click() return NewProjectPage.verify(self.browser, self.port) 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. """ 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`. """ 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:`ProjectInfoPage`. """ 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/input') element.click() title = ProjectInfoPage.project_title(project_name) return ProjectInfoPage.verify(self.browser, self.port, title)
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")
class ConfirmationPage(BasePageObject): """ Overlay displayed by ``openmdao.Util.confirm()``. """ prompt = TextElement((By.ID, 'confirm-prompt')) ok_button = ButtonElement((By.ID, 'confirm-ok')) cancel_button = ButtonElement((By.ID, 'confirm-cancel')) def __init__(self, parent): super(ConfirmationPage, self).__init__(parent.browser, parent.port) def click_ok(self): self('ok_button').click() def click_cancel(self): self('cancel_button').click()
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()
class NewProjectPage(ProjectPage): """ Page for creating a new project. """ create_button = ButtonElement((By.XPATH, '/html/body/div/div[2]/form/input')) def __init__(self, browser, port): super(NewProjectPage, self).__init__(browser, port) def submit(self): """ Clicks the 'create' button. """ self('create_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)) def create_project(self, project_name, description, version): """ Create a project, returns :class:`ProjectInfoPage`. """ self.project_name = project_name self.description = description self.version = version self.submit() title = ProjectInfoPage.project_title(project_name) return ProjectInfoPage.verify(self.browser, self.port, title)
class DialogPage(BasePageObject): """ Base for various dialog pages. """ dialog_title = TextElement((By.XPATH, '../div/span')) close_button = ButtonElement((By.XPATH, '../div/a')) def __init__(self, browser, port, locator): root = WebDriverWait( browser, TMO).until(lambda browser: browser.find_element(*locator)) super(DialogPage, self).__init__(browser, port, root) @property def is_visible(self): """ True if dialog is visible. """ return self('dialog_title').is_visible def close(self): """ Close dialog. """ self('close_button').click() def move(self, delta_x, delta_y): """ Move dialog. """ chain = ActionChains(self.browser) chain.click_and_hold(self('dialog_title').element) chain.move_by_offset(delta_x, delta_y) chain.release(None) chain.perform()
class AssemblyPage(ComponentPage): """ Assembly editor page. """ dataflow_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Dataflow']")) def show_dataflow(self): self('dataflow_tab').element.click()
class DeleteDialog(DialogPage): """ Dialog for deleting a project """ delete_button = ButtonElement( (By.XPATH, "div[@class='modal-footer']/a[text()='OK']")) cancel_button = ButtonElement( (By.XPATH, "div[@class='modal-footer']/a[text()='Cancel']")) def __init__(self, browser, port, locator): super(DeleteDialog, self).__init__(browser, port, locator) def submit(self): """Clicks the 'delete' button""" self('delete_button').click() def cancel(self): """Clicks the 'cancel' button""" self('cancel_button').click()
class ComponentPage(DialogPage): """ Component editor page. """ 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')) def __init__(self, browser, port, locator): super(ComponentPage, self).__init__(browser, port, locator) # It takes a while for the full load to complete. NotifierPage.wait(self) def get_inputs(self): """ Return inputs grid. """ self('inputs_tab').click() return self.inputs 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[0] == name: row[2] = value return found.append(row[0]) raise RuntimeError('%r not found in inputs %s' % (name, found)) def get_events(self): """ Return events grid. """ self('events_tab').click() return self.events def get_outputs(self): """ Return outputs grid. """ self('outputs_tab').click() return self.outputs def show_slots(self): """switch to slots tab""" self('slots_tab').click()
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()
class WorkflowComponentFigure(BasePageObject): """ Represents elements within a workflow figure. """ # Context menu. edit_button = ButtonElement((By.XPATH, "./ul/li[text()='Edit']")) properties_button = ButtonElement( (By.XPATH, "./ul/li[text()='Properties']")) run_button = ButtonElement((By.XPATH, "./ul/li[text()='Run']")) remove_button = ButtonElement( (By.XPATH, "./ul/li[text()='Remove from Workflow']")) @property def pathname(self): """ Pathname of this component. """ return self._pathname @pathname.setter def pathname(self, path): self._pathname = path @property def state(self): """ Exec state of this component. """ rect = self.root.find_element_by_css_selector('rect') style = rect.get_attribute('style').lower() if ('stroke: #ff0000' in style): return 'INVALID' elif ('stroke: #00ff00' in style): return 'VALID' elif ('stroke: #0000ff' in style): return 'RUNNING' else: return 'UNKNOWN' def run(self): """ Run this component. """ rect = self.root.find_element_by_css_selector('rect') chain = ActionChains(self.browser) chain.move_to_element_with_offset(rect, 15, 15) chain.context_click(None) chain.perform() time.sleep(0.5) self('run_button').click()
class DriverPage(ComponentPage): """ Driver editor page. """ parameters_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Parameters']")) workflow_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Workflow']")) objectives_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Objectives']")) constraints_tab = ButtonElement((By.XPATH, "div/ul/li/a[text()='Constraints']")) parameters = GridElement((By.ID, 'Parameters_parms')) objectives = GridElement((By.ID, 'Objectives_objectives')) constraints = GridElement((By.ID, 'Constraints_constraints')) add_parameter = ButtonElement((By.XPATH, "//div[text()='Add Parameter']")) add_objective = ButtonElement((By.XPATH, "//div[text()='Add Objective']")) add_constraint = ButtonElement((By.XPATH, "//div[text()='Add Constraint']")) def get_parameters(self): """ Return parameters grid. """ self('parameters_tab').click() return self.parameters def get_objectives(self): """ Return objectives grid. """ self('objectives_tab').click() return self.objectives def get_constraints(self): """ Return constraints grid. """ self('constraints_tab').click() return self.constraints def new_parameter(self): """ Return :class:`ParameterDialog`. """ self('add_parameter').click() return ParameterDialog(self.browser, self.port, (By.XPATH, "//div[@id='parameter-dialog']/..")) def new_objective(self): """ Return :class:`ObjectiveDialog`. """ self('add_objective').click() return ObjectiveDialog(self.browser, self.port, (By.XPATH, "//div[@id='objective-dialog']/..")) def new_constraint(self): """ Return :class:`ConstraintDialog`. """ self('add_constraint').click() return ConstraintDialog(self.browser, self.port, (By.XPATH, "//div[@id='constraint-dialog']/..")) def show_workflow(self): """switch to workflow tab""" self('workflow_tab').click() def get_workflow_component_figures(self): """ Return workflow component figure elements. """ return find_workflow_component_figures(self)
class NotifyDialog(DialogPage): """The dialog that appears when there is an error""" okButton = ButtonElement((By.ID, 'notify-ok')) def __init__(self, browser, port): # The div that contains the actual message has a div of notify-msg. # The div for the dialog is the parent of that div super(NotifyDialog, self).__init__(browser, port, (By.XPATH, '//div[@id="notify-msg"]/..')) def close(self): """ Close dialog. """ self('okButton').click()
class DialogPage(BasePageObject): """ Base for various dialog pages. """ dialog_title = TextElement((By.XPATH, '../div/span')) close_button = ButtonElement((By.XPATH, '../div/a')) def __init__(self, browser, port, locator): root = WebDriverWait( browser, TMO).until(lambda browser: browser.find_element(*locator)) super(DialogPage, self).__init__(browser, port, root) def close(self): """ Close dialog. """ self('close_button').click()
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()
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()