class AddStorage(PageObject): _header = WebElement('.modal-header') _space_name = WebElement('.modal-header .special-name') @property def title(self): header = self.driver.execute_script( '$(arguments[0]).clone().children().remove().end().text()', self._header ) return (header + ' ' + self._space_name).lower() token = command = TextLabelWebElement('textarea.token-input') _copy_btn = ButtonWebElement('.copy-btn') _gen_token_btn = ButtonWithTextWebElement('a.clickable', text='generate another token') def generate_another_token(self): self._click_on_btn('gen_token') def copy(self): self._click_on_btn('copy') def __str__(self): return 'Add storage modal for "{}"'.format(self.title)
class _Chunk(PageObject): start = TextLabelWebElement('.file-size .start') end = TextLabelWebElement('.file-size .end') _canvas = WebElement('canvas') _file_chunks = WebElement('.file-chunks') def __str__(self): return 'file blocks for {}'.format(self.parent) @property def size(self): end, unit = self.end.split() return int(end) - int(self.start), unit @property def chunks(self): file_size, _ = self.size chunks = self.driver.execute_script(_canvas_fill, self._canvas) if chunks is not False: return [(chunk[0]*file_size, chunk[1]*file_size) for chunk in chunks] else: raise RuntimeError('{} is not filled correctly: some columns ' 'are not filled with one color'.format(self)) def is_never_synchronized(self): return 'never-synchronized' in self._file_chunks.get_attribute('class')
class _SpaceRecord(PageObject, ExpandableMixin): name = id = TextLabelWebElement('.space-header.truncate', parent_name='given space record') size = TextLabelWebElement('.space-header-size') providers_count = TextLabelWebElement('.providers-count') settings = WebItem('.settings-tool .settings-dropdown', cls=_SettingsDropdown) providers = WebItemsSequence('ul.tertiary-list li.sidebar-space-provide', cls=_ProviderRecord) _toggle = ToggleWebElement('.secondary-item-container .clickable') _add_storage_btn = ButtonWebElement('ul.tertiary-list li.get-support') _set_home_btn = ButtonWebElement('.secondary-item-element.star-toggle ' '.oneicon-home-outline') _home_space_icon = WebElement('.oneicon-space-home') _home_icon = WebElement( '.secondary-item-element.star-toggle .oneicon-home') def __str__(self): return 'space record named: "{}" in {}'.format(self.name, self.parent) def is_home(self): try: _ = self._home_icon and self._home_space_icon except RuntimeError: return False else: return True def add_storage(self): self._click_on_btn('add_storage') def set_as_home(self): if not self.is_home(): self._click_on_btn('set_home')
class OPLoggedIn(object): _user_profile = WebElement('li.profile-dropdown') tabs = {'data': DataTab} def __init__(self, driver): self.web_elem = driver def __str__(self): return 'Oneprovider page' def __getattr__(self, item): return self.tabs[item](self.web_elem, self.web_elem, self) @property def user_profile(self): return UserProfile(self.web_elem, self._user_profile, self) def click_on_tab(self, name): css_sel = 'nav.primary-sidebar li a' err_msg = '{} btn in {} not found'.format(name, self) btn = find_web_elem_with_text(self.web_elem, css_sel, name.lower(), err_msg) err_msg = 'cannot click on {} in {}'.format(name, self) click_on_web_elem(self.web_elem, btn, err_msg)
class _ProviderPopup(PageObject): hostname = InputWebElement('input.provider-host-text') name = id = TextLabelWebElement('.title-label', parent_name='given provider popup') spaces = WebItemsSequence('ul li.provider-place-drop-space', cls=_SpaceRecord) _cp_hostname_btn = ButtonWebElement('.provider-host-copy-btn') _go_to_files_btn = ButtonWebElement('.drop-body .btn-go-to-files, ' '.drop-body button') _popup = WebElement('.provider-place-drop') def __str__(self): return '"{}" provider popup on {}'.format(self.name, self.parent) def is_working(self): return 'working' in self.web_elem.get_attribute('class') def is_displayed(self): try: _ = self._popup except RuntimeError: return False else: return True def copy_hostname(self): self._click_on_btn('cp_hostname') def go_to_your_files(self): self._click_on_btn('go_to_files')
class DataTopToolBar(PageObject): create_directory = ButtonWebItem('li a#create-dir-tool') create_file = ButtonWebItem('li a#create-file-tool') share_element = ButtonWebItem('li a#share-file-tool') edit_metadata = ButtonWebItem('li a#file-metadata-tool') upload_file = ButtonWebItem('li a#upload-file-tool') rename_element = ButtonWebItem('li a#rename-file-tool') change_element_permissions = ButtonWebItem('li a#lock-file-tool') copy_element = ButtonWebItem('li a#copy-file-tool') cut_element = ButtonWebItem('li a#cut-file-tool') remove_element = ButtonWebItem('li a#remove-file-tool') show_data_distribution = ButtonWebItem('li a#file-chunks-tool') _upload_input = WebElement('input#toolbar-file-browse') def upload_files(self, files): """This interaction is very hacky, because uploading files with Selenium needs to use input element, but we do not use it directly in frontend. So we unhide an input element for a while and pass a local file path to it. """ with rm_css_cls(self.driver, self._upload_input, 'hidden') as elem: elem.send_keys(files) def __str__(self): return 'toolbar in {}'.format(self.parent)
class ProgressBar(PageObject): progress_text = TextLabelWebElement('.progress-text') _progress_container = WebElement('.progress-bar') @property def progress_bar(self): style = self._progress_container.get_attribute('style') return re.search(r'width:\s*(\d+?%)', style).group(1)
class _ProviderRecord(PageObject, ExpandableMixin): name = id = TextLabelWebElement('.provider-header.truncate', parent_name='given provider record') spaces_count = TextLabelWebElement('.spaces-count') spaces = WebItemsSequence('ul.tertiary-list li.sidebar-provider-space', cls=_SpaceRecord) _provider_icon = IconWebElement('.provider-icon .oneicon') _toggle = ToggleWebElement('.spaces-count') _click_area = WebElement('.secondary-item-container') _set_home_btn = ButtonWebElement('.secondary-item-element.star-toggle ' '.oneicon-home-outline') _home_icon = WebElement('.secondary-item-element.star-toggle ' '.oneicon-home') def __str__(self): return 'provider record named: "{}" in {}'.format( self.name, self.parent) def is_home(self): home1 = 'provider-home' in self._provider_icon.get_attribute('class') try: _ = self._home_icon except RuntimeError: return False else: return True and home1 def set_as_home(self): if not self.is_home(): self._click_on_btn('set_home') def unset_from_home(self): if self.is_home(): click_on_web_elem( self.driver, self._home_icon, 'cannot click on home icon for {item} in ' '{parent}'.format(item=self, parent=self.parent)) def is_working(self): return 'color-provider-online' \ in self._provider_icon.get_attribute('class') def is_not_working(self): return 'color-provider-offline' \ in self._provider_icon.get_attribute('class')
class TransferChart(PageObject): minute = ButtonWithTextWebElement('button.btn-default', text='Minute') hour = ButtonWithTextWebElement('button.btn-default', text='Hour') active = TextLabelWebElement('button.btn-default.active') # We take only last point in the chart _speed = WebElement( '.transfers-transfer-chart .ct-series line:last-of-type') def get_speed(self): return self._speed.get_attribute('ct:value').split(',')[1]
class _ProviderRecord(PageObject): name = id = TextLabelWebElement('.one-label.truncate', parent_name='given provider record') _unsupport_space_btn = ButtonWebElement('.clickable .oneicon-leave-space') _click_area = WebElement('.clickable') def __str__(self): return 'provider record named: "{}" in {}'.format( self.name, self.parent) def unsupport_space(self): self._click_on_btn('unsupport_space')
class DirectoryTree(PageObject, ExpandableMixin): name = TextLabelWebElement('.item-label') _toggle = ToggleWebElement('.item-icon .one-icon') _header = HeaderWebElement('.secondary-sidebar-item.dir-item') _click_area = WebElement('.secondary-sidebar-item.dir-item ' '.item-click-area') _header_label = HeaderWebElement('.secondary-sidebar-item.dir-item ' '.truncate-secondary-sidebar-item') def __init__(self, *args, **kwargs): self._children = kwargs.pop('children') super(DirectoryTree, self).__init__(*args, **kwargs) def __str__(self): return 'DirectoryTree({path}) in {parent}'.format(path=self.pwd(), parent=self.parent) def __iter__(self): css_sel = 'ul.data-files-tree-list li:not(.clickable)' return (DirectoryTree(self.driver, dir_tree, self, children=dir_tree) for dir_tree in self._children.find_elements_by_css_selector(css_sel)) def __getitem__(self, name): for directory in self: if directory.name == name: return directory else: raise RuntimeError('no subdirectory named "{name}" found ' 'in {path}'.format(name=name, path=self)) def is_expanded(self): return True if 'open' in self._toggle.get_attribute('class') else False def is_active(self): return 'active' in self._header.get_attribute('class') def pwd(self): if not isinstance(self.parent, DirectoryTree): return '/' else: return '{path}{dir}/'.format(path=self.parent.pwd(), dir=self.name) @property def displayed_name_width(self): return self.driver.execute_script("return $(arguments[0]).width();", self._header_label)
class DataTabSidebar(PageObject): _space_selector = WebElement('.data-spaces-select') _root_dir = ItemListWebElement('.data-files-tree ul li') def __init__(self, *args, **kwargs): self._resize_handler = kwargs.pop('resize_handler') super(DataTabSidebar, self).__init__(*args, **kwargs) def __str__(self): return 'sidebar in {}'.format(self.parent) @property def space_selector(self): return SpaceSelector(self.driver, self._space_selector, self) @property def width(self): return self._resize_handler.location['x'] @width.setter def width(self, value): offset = value - self.width action = ActionChains(self.driver) action.drag_and_drop_by_offset(self._resize_handler, offset, 0) action.perform() @property def root_dir(self): root, root_content = self._root_dir[:2] return DirectoryTree(self.driver, root, self, children=root_content) @property def cwd(self): cwd = self._cwd(self.root_dir) if cwd: return cwd else: raise RuntimeError('no working directory found') def _cwd(self, curr_dir): if curr_dir.is_active(): return curr_dir for directory in curr_dir: cwd = self._cwd(directory) if cwd: return cwd
class FileUploader(PageObject): heading = TextLabelWebElement('.panel-heading') _progress = WebElement('.resumable-progress') _rows = ItemListWebElement('ul.resumable-list li') @property def progress(self): return ProgressBar(self.driver, self._progress, self) def is_visible(self): return 'visible' in self.web_elem.get_attribute('class') def __str__(self): return 'file uploader in {}'.format(self.parent) def __iter__(self): return (FileUploadRow(self.driver, item, self) for item in self._rows) def __getitem__(self, selector): if isinstance(selector, int): items_count = self.items_count if selector >= items_count: raise RuntimeError('requested index {index} out of bound ' '{limit}'.format(index=selector, limit=items_count)) else: return FileRow(self.driver, nth(self._rows, selector), self) elif isinstance(selector, (str, unicode)): for item in self: if item.name == selector: return item else: raise RuntimeError('unable to find "{name}" in ' '{item}'.format(name=selector, item=self)) @property def items_count(self): return len(self._rows) def scroll_to_bottom(self): self.driver.execute_script('arguments[0].scrollIntoView();', self._rows[-1])
class OZLoggedIn(object): _atlas = WebElement('.onezone-atlas') _manage_account = HeaderWebElement('header.onezone-top-bar') _panels = ItemListWebElement('.main-accordion-group') panels = { 'data space management': DataSpaceManagementPanel, 'group management': GroupManagementPanel, 'go to your files': GoToYourFilesPanel, 'access tokens': AccessTokensPanel, 'user alias': UserAliasPanel } def __init__(self, driver): self.web_elem = driver def __str__(self): return 'Onezone page' def __getitem__(self, item): item = item.lower() cls = self.panels.get(item, None) if cls: item = item.replace('_', ' ').lower() for panel in (OZPanel(self.web_elem, panel, self) for panel in self._panels): if item == panel.name.lower(): panel.__class__ = cls return panel elif item == 'manage account': return ManageAccount(self.web_elem, self._manage_account, self) elif item == 'world map': return WorldMap(self.web_elem, self._atlas, self) else: raise RuntimeError('no "{}" on {} found'.format(item, str(self)))
class FileBrowser(PageObject): empty_dir_msg = TextLabelWebElement('.empty-model-container') _empty_dir_icon = IconWebElement('.empty-dir-image') _files = ItemListWebElement('tbody tr.file-row') _files_with_metadata = ItemListWebElement('tbody tr.first-level') _bottom = WebElement('.file-row-load-more') def __str__(self): return 'file browser in {}'.format(self.parent) def __iter__(self): return (FileRow(self.driver, item, self) for item in self._files) def __getitem__(self, selector): if isinstance(selector, int): items_count = self.files_count if selector >= items_count: raise RuntimeError('requested index {index} out of bound ' '{limit}'.format(index=selector, limit=items_count)) else: return FileRow(self.driver, nth(self._files, selector), self) elif isinstance(selector, (str, unicode)): for item in self: if item.name == selector: return item else: raise RuntimeError('unable to find "{name}" in ' '{item}'.format(name=selector, item=self)) @property def files_count(self): return len(self._files) def is_empty(self): try: self._empty_dir_icon except RuntimeError: return False else: return True def get_metadata_for(self, name): for item1, item2 in iter_ahead(self._files_with_metadata): if 'file-row' in item1.get_attribute('class'): if 'file-row' not in item2.get_attribute('class'): if FileRow(self.driver, item1, self).name == name: return MetadataRow(self.driver, item2, self) else: raise RuntimeError('no metadata row for "{name}" in {item} ' 'found'.format(name=name, item=self)) def scroll_to_bottom(self): self.driver.execute_script('arguments[0].scrollIntoView();', self._bottom) @contextmanager def select_files(self): from platform import system as get_system ctrl_or_cmd_key = \ Keys.COMMAND if get_system() == 'Darwin' else Keys.LEFT_CONTROL action = ActionChains(self.driver) action.shift_down = lambda: action.key_down(Keys.LEFT_SHIFT) action.shift_up = lambda: action.key_up(Keys.LEFT_SHIFT) action.ctrl_or_cmd_down = lambda: action.key_down(ctrl_or_cmd_key) action.ctrl_or_cmd_up = lambda: action.key_up(ctrl_or_cmd_key) action.select = lambda item: action.click(item.web_elem) yield action action.perform()