asolntsev on gh-pages
upgrade to Selenide 5.20.2 (compare)
asolntsev on gh-pages
Update 2019-12-10-advent-calend… Merge pull request #15 from sva… (compare)
asolntsev on gh-pages
rename release to Selenide 5.20… (compare)
asolntsev on gh-pages
rename release to Selenide 5.20… (compare)
asolntsev on gh-pages
publish Selenide 5.20.0 release… (compare)
asolntsev on gh-pages
publish Selenide 5.20.0 release… (compare)
asolntsev on gh-pages
add appium notice (compare)
asolntsev on gh-pages
publish release notes for Selen… (compare)
asolntsev on gh-pages
publish release notes for Selen… (compare)
asolntsev on gh-pages
fix typo (compare)
asolntsev on gh-pages
fix link to littleproxy release… (compare)
asolntsev on gh-pages
publish Selenide 5.18.1 release… (compare)
asolntsev on gh-pages
publish Selenide 5.18.1 release… (compare)
asolntsev on gh-pages
fix broken link (compare)
asolntsev on gh-pages
add "Severstal" to Selenide use… (compare)
asolntsev on gh-pages
add "Whimstay" to Selenide users (compare)
asolntsev on gh-pages
add "Quality Lab" to Selenide u… (compare)
asolntsev on gh-pages
add AmazingHiring to Selenide u… (compare)
@velios Но вообще-то это неспроста, что никто этого не просил раньше. Вообще-то это странноватая проверка. Кому какая разница, какой там тэг? Важно, какой текст, визибилити и т.д. - всё, что видид юзер.
Такая потребность, потому что тестирую Spa и стандартный setValue не работает, сделал кастомный, ну и хочется обложить его проверками, что юзер не попробует применить его ни к чему кроме input или textarea, отсюда потребность
Сам кастомный код выглядит так
public static Command<SelenideElement> setValue(String value) {
return (element, locator, args) -> {
String jsCode = "return (function(webelement,text){if(webelement.getAttribute('readonly')!=undefined)return'Cannot change value of readonly element';if(webelement.getAttribute('disabled')!=undefined)return'Cannot change value of disabled element';if(typeof window.setNativeValue!=='function'){window.setNativeValue=function setNativeValue(element,value){const valueSetter=Object.getOwnPropertyDescriptor(element,'value').set;const prototype=Object.getPrototypeOf(element);const prototypeValueSetter=Object.getOwnPropertyDescriptor(prototype,'value').set;if(valueSetter&&valueSetter!==prototypeValueSetter){prototypeValueSetter.call(element,value);}else{valueSetter.call(element,value);}}} setNativeValue(webelement,text);webelement.dispatchEvent(new Event('input',{bubbles:true}));webelement.focus();var maxlength=webelement.getAttribute('maxlength')==null?-1:parseInt(webelement.getAttribute('maxlength'));webelement.value=maxlength==-1?text:text.length<=maxlength?text:text.substring(0,maxlength);return null;})(arguments[0],arguments[1]);";
String error = Selenide.executeJavaScript(jsCode, element, value);
if (error != null) {
throw new IllegalArgumentException(error);
}
Selenide.executeJavaScript("arguments[0].blur();", element);
element.shouldHave(value(value).because("это значение, которое заполняли"));
return element;
};
}
Половина кода вытянута из стандартной реализации в Selenide разумеется
Мне хочется дополнить условием
element.shouldHave(or("input or textinput", tagName("input"), tagName("textarea")))
В данном варианте проверять через локатор не вариант
execute
.
У нас проект переехал на автогенераторы классов, решал вопрос таким образом
public static Command<SelenideElement> findClassBegins(String classPart) {
return (element, locator, args) -> element.find("[class^='" + classPart + "']");
}
public static Command<SelenideElement> findClassEnds(String classPart) {
return (element, locator, args) -> element.find("[class$='" + classPart + "']");
}
public static Command<SelenideElement> findClassContains(String classPart) {
return (element, locator, args) -> element.find("[class*='" + classPart + "']");
}
public static Command<SelenideElement> findIdBegins(String idPart) {
return (element, locator, args) -> element.find("[id^='" + idPart + "']");
}
public static Command<SelenideElement> findIdEnds(String idPart) {
return (element, locator, args) -> element.find("[id$='" + idPart + "']");
}
public static Command<SelenideElement> findidContains(String idPart) {
return (element, locator, args) -> element.find("[id*='" + idPart + "']");
}
Или проверку кастомной таблицы у которой записи могут не сразу появиться
public static Command<SelenideElement> ensureHaveSize(int size) {
return (element, locator, args) -> {
ElementsCollection trs = element.findAll("tr");
if (size == 0) {
trs.first().find("td.no-data").shouldBe(visible);
} else if (size > 0) {
Awaitility.with().pollInSameThread().await("Таблица отрисовала записи").atMost(20, SECONDS).until(() ->
(trs.first().find("td.no-data").is(not(visible).because("Ожидается колчество записей больше 0")) &&
trs.size() == size));
} else {
throw new IllegalArgumentException("Введено некоректное число {" + size + "} для проверки количества записей в grid'е");
}
return element;
};
}
Даже удивлен, что еще не приходилось использовать в своих проектах
public static Command<SelenideElement> findIdEnds(String idPart) {
return (element, locator, args) -> element.find("[id$='" + idPart + "']");
}
а как выглядит ипользование этого в коде?
Awaitility.with().pollInSameThread()
:D
Я возможно что-то не так делаю. А возможно testng не лучший выбор, но без этого многопоточность не работает =(
By.id
ок. By
создавал ему какие-то проблемы, и попросил добавил аналогичные методы Selectors.byId
и т.д. ensureHaveSize
- это ведь по сути не команда (Command
), а проверка (Condition
).$(…).shouldHave(mySize(5))
Awaitility.with
там нужно, потому что селенид и так ведь умеет ждать. Вместо него достаточно просто написать trs.first().find("td.no-data”).shouldNotBe(visible)
и trs.shoudHave(size(size))
.
@asolntsev Недавно упоминали возможность метода execute для EllementCollection, мог бы пригодиться чтобы переписать такой блок во что-нибудь приличное, например trs.execute(firstNotEmptyCell(int columnIndex))
Сейчас в коде делаю это так
// Берем текст из первой не пустой ячейки в соответствующем столбце
int finalI = i;
SelenideElement cell =
trs
.stream()
.filter(tr -> tr.findAll("td").get(finalI)
.has(and("Первая заполненная ячейка", not(empty), not(text(EMPTY_NAME)))))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Столбец не содержит ни одной заполнненой ячейки"))
.findAll("td")
.get(i);
trs - это EllementsCollection из <tr> элементов
Думал изначально для этого использовать filterBy или findBy, но туда нельзя передать лямбду
Еще execute для EllementCollection был бы крут, чтобы реализовать аналог функции map из функциональных языков. Например, чтобы вернуть все ячейки первой колонки можно было бы использовать
trs.execute(map(tr -> tr.findAll("td").get(i)))
// В результате бы получился список из ячеек
.findBy(text(text))
.getText();
Сейчас как такие задачи "правильно решать" без steam() ?
rm -fr ~/.m2/repository/webdriver/resolution.properties
Вышла Selenide 5.13.0: "Вначале была подстрока”
Мы зарелизили Selenide 5.14.0: “Стабильный FOLDER”
Мы выпустили Selenide 5.15.0: "Обкликайся и обкачайся!"
Вышла Selenide 5.16.0:
"Плагины и сообщения об ошибках”
Вышла Selenide 5.16.2: "Багфиксы с антресолей"
Вышла Selenide 5.19.0: “День независимости”