Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

как обновить ссылку на елемент в Webdriver?

page-factory
page-object
selenium
webdriver
Теги: #<Tag:0x00007f7b623aa590> #<Tag:0x00007f7b623aa3d8> #<Tag:0x00007f7b623aa270> #<Tag:0x00007f7b623aa068>

(Ilja Pavlovs) #1

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

  1. надо сначало ввести данные в первый модуль о пользователе Х
  2. затем создать новый модуль для пользователя Y, при этом старый модуль будет спрятан за новым модулем, но всё ещё будет находиться в DOM.
  3. ввести данные для пользователя в новом модуле

Естественно, на эти модуль у меня один page object и когда я пытаюсь ввесту данные в новый модуль то селениум вылетает с ошибкой ElementNotVisible. Полагаю что webdriver в момент взаимодействия с новым модулем хранит в себе ссылку на первый модуль, который в свою очередь скрыт за новым модулем.

Пробовал явно вызвать инициализацию новых елементов в новом модуле, но не помогло.


(asolntsev) #2

@iljapavlovs как видите, пэдж обжекты - зло. Просто ищите нужны элементы (видимые) и их заполняйте.


(Dmitrii Demin) #3

Поддерживаю, с моей точки зрения лучше хранить final static поля типа By в классе пейдж обжекта, а затем использовать их в методах этого пейдж обжекта (исп. например Selenide и его краткие $ и $$)


(5am) #4

update: не правильно понял проблему, извиняюсь

old:
CacheLookup не помогает ?

    [FindsBy(How = How.Id, Using = "some_id")]
    [CacheLookup]
    IWebElement SomeElement;

(Pavel Yasonov) #5

Ничего не зло) У нас на странице добавился новый элемент(пусть он такой же, как и уже существующий, это не важно), а объекта в тесте для него нет. Чего ж тут хотеть?
Тут я вижу два наиболее логичных объяснения:

  1. Создать новый объект для нового диалога
  2. Модифицировать ваш Page Object так, что бы он работал только с тем объектом, поля которого visible

Идеологически правильный первый вариант. Для работы более удобный второй. Исходите от своих задач.


(Oleg Kuzovkov) #6

Я имплементировал динамическую инициализацию элементов. Т.е. тот или иной элемент будет искаться на странице перед выполнение какого либо действия над ним. Это решило все мои подобные проблемы.

PageObject - и только PageObject!


(asolntsev) #7

Так поделитесь решением, как конкретно вы это сделали?


(Goshko Nazar) #8

100500 раз обсуждалось, ответ кроется в вашем вопросе.
Page Object элемент, это описание страничных/интерфейсных моделей которые вы видите на ДАННЫЙ момент. и каждый PG объект это есть конечная модель ОДНОГО элемента, или же композиция PG показывающая сложный элемент (вся страница например)
Тогда элеменыт второго уровня объявляются атрибутами/методами, а при обращении к ним используется дексриктор, который следит - а на этой ли я странице (в питоне, хватает засунуть проверку в init, если ты не на той странице - класс не инициализируется и ты вывалишься сразу). Второе, переход между PG, осуществляется только при изменении текущего состояния страницы. К примеру, вызов окна нового письма нельзя считать переходом PG, потому что основное доступно - это просто переход между элементами, и вот когда вы на элементе, вы должны инициализить эту часть PG и следить, что:

  • пока я вижу скажем татл этого окна, я могу обращаться к элементам этого окна, если тайтл пропал - я где то в другом месте.
    Для слежения за элементами, очень подходит тулзы от selenium, которые возвращают статус элемента по его состоянию (выделен, виден, кликабелен и т.д)

(Oleg Kuzovkov) #9

У меня в фреймворке есть Core пакет, где у меня ядро всего что нужно. В этом пакете есть WebPage and WebItem основные классы, в которых храняться все основные инструменты управление.

Все элементы страницы у меня класифицированы и разделены на Кнопки, ПопапЛисты, Грид и т.д., которые в свою очередь унаследованы от WebItem. Это позволяет мне иметь набор тех или иных уникальных методов для каждого типа отдельно.

Так вот в WebItem классе, у меня есть конструктор, который я использую что бы присвоить внутреннему полю объекта “By byId;” значение из @FindBy() аннотации, которое я вынимаю в конструкторе страницы класса WebPage используя reflection. На этом создание объекта страницы и инициализация его полей заканчиваеться. Т.е. я не ищу сразу елементы аля Driver.findElement при создании объкта страницы, а я просто запоминаю идетнификаторы этих элементов внутри их объектов в приватных полях.
После, когда мне нада сделать click(), sendKeys() и т.д., я вызываю getItem() метод внутри переписаных методов click(), sendKeys() в WebItem классе, где и происходит реальный поиск элемента. Конечно это все дополнено explicit and implicit вейтами, что бы элемент был в нужном состоянии, и что бы страница прогрузилась.

Таким образом мне пофик что проиходит со страницой во время выполнения скрипта, и я уверен что действие выполниться на 100%, не зависимо от обновления DOM структуры страницы.

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


(Dmitrii Demin) #10

Примерно то же самое, о чем писал я:

  1. В классе PG (или блока элементов) храним поля типа By (константы, static)
  2. Там где нужно (в методах класса) юзаем к примеру Селенид: $(LOGIN_BUTTON).click();

Немного не по теме, но вдруг кто не знает:

  1. Если вдруг приспичило найти элемент через цепочку предшествующих и не хочется городить .findElement().findElement()…etc , то берем наши константы типа By и объединяем в ByChained chain = new ByChained (GRANDPA_ELEMENT, FATHER_ELEMENT, SON_ELEMENT);
  2. Локатор chain передаем куда нужно ( в findElement или селенидовский $/$$)