Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jun 12 19:23

    asolntsev on gh-pages

    publish Selenide 5.22.0 release… (compare)

  • Jun 10 10:42

    asolntsev on gh-pages

    publish Selenide 5.22.0 release… (compare)

  • Jun 02 15:25

    asolntsev on gh-pages

    the historical moment: disable … (compare)

  • May 19 21:06

    asolntsev on gh-pages

    Update faq.md Fix typo (compare)

  • May 17 17:12

    asolntsev on gh-pages

    publish release notes for Selen… (compare)

  • May 16 19:20

    asolntsev on gh-pages

    upgrade kramdown to 2.3.1 which… (compare)

  • May 16 13:44

    asolntsev on gh-pages

    publish release notes for Selen… (compare)

  • Apr 22 16:37

    asolntsev on gh-pages

    released Selenide 5.20.4 (compare)

  • Apr 20 17:11

    asolntsev on gh-pages

    released Selenide 5.20.3 (compare)

  • Apr 15 21:46

    asolntsev on gh-pages

    upgrade to Selenide 5.20.2 (compare)

  • Apr 02 06:57

    asolntsev on gh-pages

    Update 2019-12-10-advent-calend… Merge pull request #15 from sva… (compare)

  • Apr 02 06:57
    asolntsev closed #15
  • Apr 01 22:31
    svasenkov opened #15
  • Mar 26 07:26

    asolntsev on gh-pages

    rename release to Selenide 5.20… (compare)

  • Mar 24 21:25

    asolntsev on gh-pages

    rename release to Selenide 5.20… (compare)

  • Mar 24 21:17

    asolntsev on gh-pages

    publish Selenide 5.20.0 release… (compare)

  • Mar 24 21:14

    asolntsev on gh-pages

    publish Selenide 5.20.0 release… (compare)

  • Feb 24 09:05

    asolntsev on gh-pages

    add appium notice (compare)

  • Feb 24 08:34

    asolntsev on gh-pages

    publish release notes for Selen… (compare)

  • Feb 23 23:04

    asolntsev on gh-pages

    publish release notes for Selen… (compare)

Andrei Solntsev
@asolntsev
@velios сейчас такого нет. Наверное, можно сделать. Но как-то пока не просил никто.
Andrei Solntsev
@asolntsev
@velios погоди-ка, я что-то туплю. Ведь тебе нужно не действие с коллекцией, а проверка, так? Тогда просто вызови $$().shouldHave(new ТвойCollectionCondition())
Denis Gaievskyi
@dengayevskiy-sb
@asolntsev кое как запустил тесты в контейнере, но скачивание через прокси по прежнему не работает :/ Похоже, я плохо понимаю механизм его работы. Если мне нужен вебдрайвер из контейнера, я должен сам создать и стартонуть селенид прокси?
    SelenideProxyServer proxyServer;

    @Container
    public BrowserWebDriverContainer chrome =
            new BrowserWebDriverContainer()
                    .withCapabilities(new ChromeOptions());

    @BeforeEach
    public void before() {
        SelenideConfig defaults = new SelenideConfig();
        proxyServer = new SelenideProxyServer(defaults, null);
        RemoteWebDriver driver = chrome.getWebDriver();
        WebDriverRunner.setWebDriver(driver, proxyServer);
        proxyServer.start();

        Configuration.timeout = globalTimeout;
        Configuration.baseUrl = collab_url;
        Configuration.fileDownload = FileDownloadMode.PROXY;
        Configuration.proxyEnabled = true;
        Configuration.proxyHost = "0.0.0.0";
        Configuration.fastSetValue = true;
        Configuration.startMaximized = true;
    }
velios
@velios
@asolntsev Если есть возможность расскажите, когда проверяем лямбдами коллекцию, то для CollectionContition'ов allMatch, anyMatch, noneMatch, то как Predicate используется WebElement из Selenium, а не SelenideElement? Интересно почему так сделано?
@CheckReturnValue
    public static CollectionCondition allMatch(String description, Predicate<WebElement> predicate) {
        return new AllMatch(description, predicate);
 }
velios
@velios

Приведу примеры, может я не правильно это все использую просто. Сейчас пишу проверку так

trs.shouldBe(allMatch("all value same in column",
                    e -> e.findElements(By.tagName("td")).get(i).getText().equals(cellValue)));

если бы был SelenideElement представляю можно было писать так

trs.shouldBe(allMatch("all value same in column",
                    e -> e.findAll("td").get(i).has(text(cellValue));

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

Oleksii Cherevatyi
@JustAlexOne

@JustAlexOne крутое расследование! Я в своё время похожую багу в селениум зарепортил: SeleniumHQ/selenium#489

@asolntsev ага, я тоже на стековерфлоу видел у людей проблемы были с медленным резолвом хостнейма. Я так понимаю, на стороне селенида ничего не нужно делать?

Andrei Solntsev
@asolntsev
@JustAlexOne Наверное, мы могли бы взять тот же фикс, что Баранцев сделал в селениеме, и запихать его в селенид или browserup-proxy.

@dengayevskiy-sb Боюсь, так просто не получится. Чтобы браузер использовал прокси, надо ему при создании вебдрайвера задать соответствующую опцию (типа options.setProxy(selenideProxy.createSeleniumProxy());).

Если же браузер изначально создан внутри TestContainers, он ничего про селенидовский прокси знать не может.

Andrei Solntsev
@asolntsev
@JustAlexOne Может удастся задать селенидовский прокси через capabilities? Как-то так:
  @Rule
  public BrowserWebDriverContainer chrome =
      new BrowserWebDriverContainer()
          .withCapabilities(как-то здесь задать свой прокси)
          .withRecordingMode(BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL, new File("build"))
          .withCapabilities(DesiredCapabilities.chrome());
@JustAlexOne А вообще, я подумаю сделать (как минимум для хрома) третий способ скачивания файлов: не через прокси, а через CDP. Это по идее должно решить проблему.
@velios Вся фишка селенидовских проверок в том, что они могут подождать.
Если в allMatch (как и в других CollectionCondition) дать доступ к SelenideElement, люди смогут вызывать селенидовские методы типа .shouldHave(…), и тогда получится странная ситуация: внутри “ожидающего” метода allMatch будут в цикле вызываться другие “ожидающие” методы shouldHave. Сложно и непредсказуемо.
Andrei Solntsev
@asolntsev
@velios А может быть, ты расскажешь, каких именно методов не хватает в WebElement? Просто has(text())) действительно не очень много.
Denis Gaievskyi
@dengayevskiy-sb
@asolntsev то есть, нет возможности использовать прокси селенида для скачивания и тест контейнеры :(
Andrei Solntsev
@asolntsev
@dengayevskiy-sb Секунду, эту опцию пробовали?
@Rule
  public BrowserWebDriverContainer chrome =
      new BrowserWebDriverContainer()
          .withCapabilities(как-то здесь задать свой прокси)
Denis Gaievskyi
@dengayevskiy-sb

@asolntsev попробовал

SelenideProxyServer proxyServer = getProxy();

    public SelenideProxyServer getProxy() {
        SelenideConfig defaults = new SelenideConfig();
        defaults.baseUrl(collab_url);
        defaults.proxyEnabled(true);
        defaults.fileDownload(FileDownloadMode.PROXY);
        defaults.proxyHost("127.0.0.1");
        defaults.proxyPort(8888);
        return proxyServer = new SelenideProxyServer(defaults, null);
    }

    @Container
    public BrowserWebDriverContainer chrome =
            new BrowserWebDriverContainer()
                    .withCapabilities(new ChromeOptions().setProxy(proxyServer.createSeleniumProxy()));

    @BeforeEach
    public void before() {
        proxyServer.start();
        RemoteWebDriver driver = chrome.getWebDriver();
        WebDriverRunner.setWebDriver(driver, proxyServer);

        Configuration.baseUrl = collab_url;
        Configuration.fileDownload = FileDownloadMode.PROXY;
        Configuration.proxyEnabled = true;
        Configuration.proxyHost = "127.0.0.1";
        Configuration.proxyPort = 8888;
        sessionApi.createAdmin();
    }

Теперь стало падать раньше по тесту на попытке засетить куки -> org.openqa.selenium.InvalidCookieDomainException: invalid cookie domain

velios
@velios

@velios А может быть, ты расскажешь, каких именно методов не хватает в WebElement? Просто has(text())) действительно не очень много.

Методов для навигации. Мы например используем такую штуку для выставления стилей компонентам https://github.com/Jarzka/stylefy Она добавляет всем классам рандомную каждый раз изменяющуюся часть. Приходится искать вложенные элементы через e.find("[class*='<class-part>']"). В webelement так нельзя насколько мне известно. Те же методы поиска оборачиваются в execute блоки. В webelement так нельзя. pseudo-элементы и вот это все.

Методов для проверки. Те же Condition. or, and, not блоки и прочее, что есть в SelenideElement

  • уже по мелочи, некрасиво что в коде смешиваются разные синтаксисы selenium'а и selenide
Denis Gaievskyi
@dengayevskiy-sb
image.png
@asolntsev убрал сеттинг кук, чтобы увидеть реальную ошибку
Andrei Solntsev
@asolntsev
@dengayevskiy-sb Думаю, что айпишник 127.0.0.1 не подходит для прокси. Ведь браузер бежит внутри контейнера, и для него это свой собственный адрес, а не адрес машины с тестами.
Нужно прописать другой айпишник - вроде бы в докере есть какой-то специальный апйпишник, под который контейнеру видна хостовая машина.
Andrei Solntsev
@asolntsev
@velios А ты в курсе, что WebElement очень просто легко превратить в SelenideElement? Это ведь решит твою проблему, так?
SelenideElement se = $(webElement);
@velios Я завёт тикет для обсуждения: selenide/selenide#1135
Denis Gaievskyi
@dengayevskiy-sb
@asolntsev Нашел параметр host.testcontainers.internal, который должен дать доступ из контейнера к хосту, но ничего не изменилось. Меня еще слегка смущает, что я должен дважды создавать конфигурацию селенида, это я что-то делаю не так или без этого не создать прокси сервер?
    private static int freePort;
    private static SelenideProxyServer proxyServer;

    static {
        freePort = SocketUtils.findAvailableTcpPort();
        proxyServer = getProxy();
        Testcontainers.exposeHostPorts(freePort);
    }

    public static SelenideProxyServer getProxy() {
        SelenideConfig defaults = new SelenideConfig();
        defaults.baseUrl(collab_url);
        defaults.proxyEnabled(true);
        defaults.fileDownload(FileDownloadMode.PROXY);
        defaults.proxyHost("host.testcontainers.internal");
        defaults.proxyPort(freePort);
        return new SelenideProxyServer(defaults, null);
    }

    @Container
    public BrowserWebDriverContainer chrome =
            new BrowserWebDriverContainer()
                    .withCapabilities(new ChromeOptions().setProxy(proxyServer.createSeleniumProxy()));

    @BeforeEach
    public void before() {
        proxyServer.start();
        RemoteWebDriver driver = chrome.getWebDriver();
        WebDriverRunner.setWebDriver(driver, proxyServer);

        Configuration.baseUrl = collab_url;
        Configuration.fileDownload = FileDownloadMode.PROXY;
        Configuration.proxyEnabled = true;
        Configuration.proxyHost = "host.testcontainers.internal";
        Configuration.proxyPort = freePort;
        sessionApi.createAdmin();
    }
Denis Gaievskyi
@dengayevskiy-sb

Заметил еще, когда задаю в капабилитис прокси через метод селенида proxyServer.createSeleniumProxy(), то в хроме создаются SSL и HTTP прокси, я получаю кучу SSL ошибок и урлы не открываются. Если же в капабилитис задать прокси напрямую вот так:

@Container
    public BrowserWebDriverContainer chrome =
            new BrowserWebDriverContainer()
                    .withCapabilities(new ChromeOptions().setProxy(new Proxy()
                            .setHttpProxy("host.testcontainers.internal:" + freePort)));

То все заработает ок, а тест завалится в самом конце на скачивании файла
java.io.FileNotFoundException: Failed to download file {a[title*='SQL']} in 10000 ms.Intercepted 0 responses

Andrei Solntsev
@asolntsev
@dengayevskiy-sb :) Ну да, похоже, что SSL ошибок нет, потому что прокси не используется. Адрес-то, наверное, https?
@dengayevskiy-sb Проблему с двойной инициализацией конфига легко решить. Прокси использует всего две настройки оттуда, достаточно только их задать: proxyHost и proxyPort.
А можно ещё проще: сначала задать все настройки в Configuration, а потом создать прокси так:
SelenideProxyServer proxy = new SelenideProxyServer(new StaticConfig(), null);
Denis Gaievskyi
@dengayevskiy-sb
@asolntsev да, адрес у меня https.
Denis Gaievskyi
@dengayevskiy-sb
    @Container
    public BrowserWebDriverContainer chrome =
            new BrowserWebDriverContainer()
                    .withCapabilities(new ChromeOptions().setProxy(new Proxy()
                            .setSslProxy("host.testcontainers.internal:" + freePort))
                            .setAcceptInsecureCerts(true));
@asolntsev В итоге заработало вот так. Возможно, стоит добавить сюда https://github.com/selenide-examples/testcontainers пример с прокси, чтобы мои страдания не пропали даром?
velios
@velios

@velios А ты в курсе, что WebElement очень просто легко превратить в SelenideElement? Это ведь решит твою проблему, так?

SelenideElement se = $(webElement);

Да, решает, теперь кажется странным почему прямой cast типов не работает в лямбде как я это ожидаю...

Вот рабочий пример

trs.shouldBe(allMatch("all value same in column",
                    el -> {
                        SelenideElement se = $(el);
                        return se.findAll("td").get(finalI).has(text(cellValue));
                    }));

вот как кажется его можно было бы упростить, но почему-то так не работает

trs.shouldBe(allMatch("all value same in column",
                    el -> (SelenideElement) el.findAll("td").get(finalI).has(text(cellValue))));
Andrei Solntsev
@asolntsev

Возможно, стоит добавить сюда https://github.com/selenide-examples/testcontainers пример с прокси, чтобы мои страдания не пропали даром?

Да, хорошая идея, попробую добавить.

вот как кажется его можно было бы упростить, но почему-то так не работает

Нет, так нельзя. Ты пытаешься закастить WebElement el к типу SelenideElement, а он же не такого типа.

Denis Gaievskyi
@dengayevskiy-sb
@asolntsev я могу сделать пулл реквест туда по горячим следам
Andrei Solntsev
@asolntsev
Давай! Супер.
Denis Gaievskyi
@dengayevskiy-sb
velios
@velios
Простите за глупый вопрос, как внутри shouldHave проверить что элемент имеет определенный тэг? .shouldHave(attribute("tag", "input")) не срабатывает =(
Andrei Solntsev
@asolntsev
@velios Похоже, такого условия сейчас нет. Прикинь. :)
Надо добавить что-то типа $.shouldHave(tagName(“h1”)).
@velios Но вообще-то это неспроста, что никто этого не просил раньше. Вообще-то это странноватая проверка. Кому какая разница, какой там тэг? Важно, какой текст, визибилити и т.д. - всё, что видид юзер.
Oleksii Cherevatyi
@JustAlexOne
Докину ещё в топку насчёт тега. Название тега может быть просто частью локатора, и тем самым и проверится сам тег
Andrei Solntsev
@asolntsev
Ну да, обычно так и происходит.
velios
@velios

@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")))

В данном варианте проверять через локатор не вариант

velios
@velios

@velios Похоже, такого условия сейчас нет. Прикинь. :)
Надо добавить что-то типа $.shouldHave(tagName(“h1”)).

Думаю раньше много чего не просили, пока не появился execute. Эта штука gamechanger, то что раньше реализовывать казалось надуманным, теперь имеет смысл

velios
@velios
Я пытался Kotlin в проект затащить, ради ClassExtensions, так мне не хватало execute =)
velios
@velios
В данном чате же много практиков Selenide. Многие стали использовать execute, как он появился? У меня это одна из самых частых теперь комманд. Рассматривали возможность ввести для нее короткий alias? e - например
Andrei Solntsev
@asolntsev
@velios Хм… Всё это очень интересно… :)
game changer? Кто бы мог подумать…
нет, я в своих проектах до сих пор не использовал execute.
velios
@velios

У нас проект переехал на автогенераторы классов, решал вопрос таким образом

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;
        };
    }

Даже удивлен, что еще не приходилось использовать в своих проектах

Boris Osipov
@BorisOsipov
Awaitility.with().pollInSameThread() :D