Skip to content

framework v.1 - #1

Open
KyxecGit wants to merge 4 commits into
mainfrom
new
Open

framework v.1#1
KyxecGit wants to merge 4 commits into
mainfrom
new

Conversation

@KyxecGit

@KyxecGit KyxecGit commented Oct 4, 2024

Copy link
Copy Markdown
Owner

No description provided.

Comment thread browser/browser.py Outdated
class Browser:
def __init__(self):
self.driver = BrowserFactory.get_driver()
self.wait = WebDriverWait(self.driver, 10)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 - хардкод, в конфиг

Comment thread browser/browser.py Outdated
Comment on lines +13 to +14
def get_driver(self):
return self.driver

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно для наглядности поле self.driver сделать protected

Comment thread browser/browser.py
return self.driver

def get(self, url):
self.logger.info(f"Переход по адресу: {url}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше в коде придерживаться английского языка + с русским языком потом можешь говна похавать в плане кодировок, но в современном мире это редкость уже

Comment thread browser/browser.py Outdated
Comment on lines +60 to +67
def switch_to_the_tab(self, current_window_handle):
self.logger.info("Переключение на новую вкладку")
new_window_handle = None
for handle in self.driver.window_handles:
if handle != current_window_handle:
new_window_handle = handle
break
self.driver.switch_to.window(new_window_handle)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new_window_is_opened(current_handles)

Есть еще явное ожидание для открытия новой вкладки

Comment thread browser/factory.py Outdated
Comment on lines +6 to +8
@staticmethod
def get_driver():
driver = Chrome()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И прям вообще удалось без опций сконфигурировать? Ну давай хотя бы развер окна выставим чтобы он на любом окружении был всегда одинаковый. Если не выставлять размер браузера, то в headless режиме они могут быть очень очень маленькими по умолчанию и никакой элемент у тебя не будет находиться (почитай что такое --headless режим, если еще не знаешь)

Comment thread tests/test_auth.py Outdated
Comment on lines +7 to +8
LOGIN = "admin"
PASS = "admin"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не сказал бы что это секретные данные и их нужно как-то прятать. Видно, что они тестовые. Но давай с целью тренировки положим это в переменные окружения и почитай про переменные окружения, почему секретные данные кладут именно туда

Comment thread tests/test_content.py Outdated
Comment on lines +24 to +26
image_1 = image_sources[0]
image_2 = image_sources[1]
image_3 = image_sources[2]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image_1, image_2, image_3 = image_sources

Пользуйся распаковкой в таких места

Comment thread tests/test_image.py Outdated
Comment on lines +33 to +35
self.upload_image_page.upload_image2(path)

actual_name = self.upload_image_page.get_image_text()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нет обработки диалогового окна системы

Comment thread utils/logger.py Outdated
Comment on lines +4 to +5
@staticmethod
def setup_logger(name='framework_logger', log_file='framework.log', level=logging.DEBUG):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name, log_file, level - лучше вынести все в конфиг

Comment thread utils/logger.py
Comment on lines +4 to +20
@staticmethod
def setup_logger(name='framework_logger', log_file='framework.log', level=logging.DEBUG):
logger = logging.getLogger(name)

if not logger.handlers:
logger.setLevel(level)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setFormatter(formatter)

console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)

logger.addHandler(file_handler)
logger.addHandler(console_handler)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

кодировки, строки форматтера, файл, имя - все нужно вынести в конфиг

Comment thread browser/browser.py Outdated
Comment on lines +64 to +66
self.wait.until(
lambda driver: len(driver.window_handles) > 1
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно попробовать уже готовое явное ожидание
new_window_is_opened(current_handles)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread browser/browser.py Outdated
Comment on lines +97 to +98
def find_element(self, by, value):
return self._driver.find_element(by, value) No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Этот метод не описываем, так как не используем. Если тебе "пришлось" его описать, значит ты допустил одну из популярных ошибок. Обрати внимание внимательно на то какой конкретно объект ты используешь в каждый конкретный момент времени (оригинальный или оберточный)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread browser/factory.py Outdated
@staticmethod
def get_driver():
options = Options()
options.add_argument("--window-size=1920,1080")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опции - в конфиг

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread elements/base_element.py Outdated
self.driver = driver.get_driver()
self.locator = locator
self.description = description
self.wait = WebDriverWait(self.driver, 10)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 хардкод, не исправил

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread elements/base_element.py Outdated

class BaseElement:
def __init__(self, driver, locator, description=None):
self.driver = driver.get_driver()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сохраняем мы везде оберточный драйвер, а уже когда нужно - получаем через него оригинальный
Если ты сохранишь оригинальный объект, то доступ к обертке утеряешь на протяжении всего класса BaseElement

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread pages/menu_page.py Outdated
Comment on lines +20 to +22



Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

пустые строки

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread pages/scroll_page.py Outdated

def get_all_paragraphs(self):
self.logger.info("Получение всех параграфов на странице")
inner_html = self.driver.execute_script(f'return document.querySelector("div.scroll").innerHTML;')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ты правильно сделал что стал получать innerHTML, только ты переусложнил js-код
element.get_attribute("innerHTML") - найди элемент через явное ожидание и у него получи свойство, не нужно искать его через js
У элементов масса аттрибутов (не все они отображаются в html коде, некоторые скрыты, но они есть)
также есть схожее свойство - outherHTML, почитай разницу

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread tests/test_handler.py Outdated
Comment on lines +49 to +52
self.logger.info("Закрыли вкладку с заголовком: %s", self.NEW_WINDOW_TITLE)

browser.close_tab_by_title(self.NEW_WINDOW_TITLE)
self.logger.info("Закрыли вкладку с заголовком: %s", self.NEW_WINDOW_TITLE)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно сказать, что логи низкого уровня - это логи в твоем фреймворке. Средний уровень - page object, а самый верхний - тесты. Они вызывают друг друга как матрешка. Видеть 100 логов на разных уровнях к одному и тому же действию иногда тоже избыточно

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~

Comment thread tests/test_image.py Outdated
logger = Logger.setup_logger()


@pytest.mark.parametrize('path, image_name', [("C:\image.jpg", "image.jpg")])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Абсолютные пути - очень жесткая ошибка. Используем только относительные, так как только они будут работать на разных системах

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread tests/test_slider.py Outdated

self.actions_page = SliderPage(browser)
self.actions_page.wait_for_open()
slider_value = random.randint(0, 8)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0, 8 лучше вынести в константы или хотя бы переменные чтобы было нагляднее

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread browser/browser.py
Comment on lines +95 to +98
def new_window_is_opened(self, current_handles):
def _predicate(_driver):
return len(_driver.window_handles) > len(current_handles)
return _predicate No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для этого есть готовое явное ожидание (правда оно еще переключается на новую вкладку)
Я не понимаю, зачем внутри вложенная функция и зачем наружу ее отдавать. Исходя из названия метода ты должен возвращать bool. Что-то перемедурил)

Comment thread browser/browser.py
Comment on lines +64 to +65
# Используем явное ожидание для новой вкладки
self.wait.until(self.new_window_is_opened([current_window_handle]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А готовое не подошло?
new_window_is_opened(current_handles)

Comment thread elements/base_element.py
Comment on lines +26 to +28
@property
def driver(self):
return self.driver_wrapper.get_driver()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Считаю что не совсем правильно получать оригинальный объект драйвера через BaseElement, я бы убрал этот метод. Этот метод должен быть у нашей обертки над браузером
Но логику ты понял, чтобы правильно отработало явное ожидание - ты должен передать в класс WebDriverWait оригинальный driver, а не твой оберточный

Comment thread elements/input.py
Comment on lines +12 to +13
def clear_input(self, input_field):
input_field.clear() No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше переименовать в просто clear. Это нативное название метода (так он называется в оригинальном selenium). Плюс слово Input лишнее по причине того, что это метод объекта input (то есть то что это действие над input и так само собой разумеющееся)

Comment thread elements/slider.py
Comment on lines +6 to +8
class Direction(Enum):
LEFT = 'left'
RIGHT = 'right'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужно сделать string enum. Почитай отличия от обычного

Comment thread pages/hover_page.py

def hover_over_figure(self, index):
self.logger.info(f"Наведение на фигуру с индексом {index}")
figure_template = (By.XPATH, self.FIGURE_TEMPLATE.format(index))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут ты захардкодил By.XPATH внутри метода. А тип локатора должен быть всегда рядом с самим локатором. Тут или тебе нужно правильно воспользоваться кодом в инициализаторе BasePage (убрать локатор вовсе, он выставится XPATH автоматически) или указывать тип локатора вместе с форматируемой строкой и не хардкодить его в методе тогда
Локатор изменят - а нам придется искать где же там еще куски по методам еще разбросаны

Comment thread pages/image_page.py
Comment on lines +50 to +51
pyautogui.write(image)
pyautogui.press('enter')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше это вынести в отднльную утилитку, например PyAutoGuiUtils или UploadFileUtils или еще как-то

Comment thread pages/scroll_page.py
Comment on lines +38 to +39

locator = (By.XPATH, self.PARAGRAPH.format(current_count))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By.XPATH захардкожено. Читай коммент выше. Нужно поправить везде

Comment thread utils/config.py
Comment on lines +2 to +3

CHROME_OPTIONS = "--window-size=1920,1080"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опции лучше хранить в списке, так как их часто добавляю новые

Comment thread tests/test_image.py
Comment on lines +8 to +9
class TestUploadImage:
logger = Logger.setup_logger()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я конечно понимаю, что логгер сингтон и каждую инициализацию логгера ты получаешь все тот же объект. Думаю это не ошибка, но вот еще подход: можно передавать логгер через фикстуру, которая произведет инициализацию единожды

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants