“element is not attached to page document” при клике чекбокса

Добрый день, всем.

Есть такой метод, который перестал работать

	public void selectAllСheckboxes() {
		createOrEditMonitorForm.findElements(By.xpath(".//table//input")).
			forEach(e -> e.click());
	}

Тест падает с “element is not attached to page document” при клике второго чекбокса. Сама причина падения понятна - после нажатия первого чекбокса, страница перерендеривается и второй чекбокс просто не находит. Но как это исправить я пока не могу понять

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

1 лайк

Привет, я как то ответил большим постом по этой проблеме - Общий алгоритм решения "element is not attached to page document" - #5 от пользователя xotabu4

спасибо за совет, так и переделал

	public void selectAllCheckboxes() {
		int monitorsAmount = createOrEditMonitorForm.findElements(By.xpath(".//table//tbody/tr")).size();
		for (int i = 1; i <= monitorsAmount; i++)
			createOrEditMonitorForm.find(By.xpath(".//tbody//tr[" + i + "]//input")).click();		
	}
1 лайк

Ну вам бы еще вынести локаторы в yaml и дать названия им осознанные, было бы вообще отлично

А если использовать отложенную инициализацию, то такой проблемы вообще не будет.
И не придётся костылить какие-то циклы.

Не согласен что это костыль. Это скорее “понимание происходящих процессов на странице”. Ну вот взяли вы например и захотели все чекбоксы нажать и что теперь в тупую нажимать все подряд и если этот метод не сработал то любой другой алгоритм это костыль? Нет. Мы понимаем что есть перерендер, что id уже другой. Взяли и переписали метод и все. А отложенная инициализация для пачки чекбоксов как у вас бы выглядела?

Делать отложенную инициализацию для пачки чек-боксов не надо, её надо делать для всех элементов.
Сейчас 99% страниц нормальных приложений почти после любого действия что-то меняют на лету, будете писать циклы на каждый такой случай? И как читать такой код, где стопицот циклов?

Любой цикл вносит кучу лишних строк в код. А чем больше строк - тем дольше и труднее этот код читается.

Из-за использования цикла нужно теперь думать, как избавиться от дублирования локаторов. Один костыль порождает другой.

А так, могло бы быть лаконично написано что-то типа (не Java, но там тоже всё это по аналогии делается же)

[FindBy(Id = "fullRejectComment", ParentElement = CdOrderModalBlockName)]
        [ElementTitle("Чек-боксы blahblah")]
        private AList<ACheckBox> _cbList;

где-то вверху страницы, и потом легко можно было бы пройтись по всем чек-боксам в одну строку:

_cbList.ForEach(cb => cb.Select());

Всё это конечно субъективно, и каждый в праве писать как он хочет, но на мой взгляд 1 строка кода понятнее и удобнее.

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

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

Ну да, можно и на машине ездить без знания как устроен двигатель

Лично моё мнение - зачем создавать локатор, если элемент используется только один раз в одном опредёленном методе? Насколько разрастётся страница, если я все используемые локаторы - вынесу в отдельные переменные.

1 лайк

Сегодня в одном месте, завтра в двух, послезавтра в трёх. Можно сразу написать так, чтобы не пришлось в будущем переписывать.

Да и как-то удобнее, когда локаторы хранятся в одном месте, а не размазаны по всему коду в PO.

Субъективно, конечно.

У меня есть PO с 600-700 строками кода, и там всё очень удобно разложено по регионам. Не нужное всегда свёрнуто.

1 лайк

Я создал отдельную переменную для чекбоксов
@FindBy(xpath = “//*[@id=‘monitor-form’]//table//input”)
private ListWebElementFacade submonitorsCheckboxes;
Но метод все равно падает, с тем же эксепшином.
Что вы имеете ввиду под “отложенной инициализацией”?

Для этого нужно использовать хотя бы штатную PageFactory (для начала - потом придёт понимание что нужно писать своё)
Просто так эта магия не будет работать.

Подробнее тут PageFactory · SeleniumHQ/selenium Wiki · GitHub
Ну или здесь Tools QA - Page Not Found

а в PageFactory не возникает StaleReference ?))

Если написать свой декоратор, нет.

согласен … вообше дофига кто хейтит @FindBy - но если переписать initElements со своей реализацией декоратора - то вполне хорошее решение

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

Из полезных вариантов можно выделить:

  1. Название родительского элемента - полезно в так называемых блоках, чтобы элементы искались в нем, а не на всей странице.

  2. Таймаут поиска элемента. Можно указать у конкретного элемента, сколько времени будем его искать прежде чем выкинем эксепшн.

Лично я использую первый вариант, второй редко пока где пригождался.

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

Ну и так далее, много полезного можно придумать :slight_smile:

1 лайк