Ajuda: como verificar se um elemento apareceu na tela?

Pessoal, estou com um problema para verificar se uma div de erro aparece na tela…

Geralmente os waits esperam até o elemento aparecer, e se ele não aparecer até um certo tempo, dispara um erro de elemento não encontrado. Porém, o que eu preciso fazer é o contrário: se tudo der certo, o elemento não irá aparecer, mas se aparecer quero que o teste quebre. Além disto, o elemento só aparece por uns 6 segundos, depois ele some.

alt text

@FindBy(css = ".alert-danger")
    protected WebElement errorAlert;

Este elemento é o mesmo em todas as telas, então a minha ideia seria, após o teste, esperar 1 segundo e verificar se o elemento está visível. Se sim, quebra o teste, se não, continua. O problema é que esta div não existe até que o erro aconteça, então se eu faço a instrução abaixo também dá erro:

waitExplict(1000);
if (errorAlert.isDisplayed()){
    System.out.print("teste quebrou!");
}
no such element: Unable to locate element: {"method":"css selector","selector":".alert-danger"}

Como eu poderia verificar a presença do elemento neste caso?

@Bruno-Fernandes na classe ExpectedConditions tem como você fazer not de uma condição, e também existem uma condições negativas como invisibilityOfElement. Dá pra seguir por essa linha.

Essa parada aí é Java? Se for, tem uma lib MUITO foda chamada Awaitility: https://github.com/awaitility/awaitility, com ela você pode criar um wait pra qualquer coisa.

Usava ela direto nos testes, principalmente pra testes com banco de dados. Como as operações no banco não eram imediatas, sempre precisava de um certo wait, mas Thread.sleep é tosco demais, é inadmissível. Com isso, eu conseguia criar uns waits esperando até que um determinado registro estivesse no banco, por exemplo. Nela vc pode customizar o tempo de polling, timeout, a porra toda. É muito útil :+1:

Não sei se assim daria certo, mas pode ser uma solução.

    WebDriverWait wait = new WebDriverWait(driver, 1);
    wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("#.alert-danger")));

Sim, é java.

@stefanteixeira disse em Ajuda: como verificar se um elemento apareceu na tela?:

na classe ExpectedConditions tem como você fazer not de uma condição, e também existem uma condições negativas como invisibilityOfElement

Eu tentei estas opções, mas também dá o mesmo problema. O not(visibilityOf(errorAlert))aparentemente assume que existe e espera sumir, aí dá problema porque ele não existe.

public void waitForElementPageToLoad(ExpectedCondition pageLoadCondition) {
        WebDriverWait wait = new WebDriverWait(SeleniumDriver.getDriver(), MAX_LOAD_TIMEOUT);
        wait.until(pageLoadCondition);
    }

waitForElementPageToLoad(not(visibilityOf(errorAlert)));

@tuliobluz , não cheguei a tentar com o presenceOfElementLocated(), pode ser que funcione, já que pelo nome ele deve verificar se o elemento existe na página e não se está visível ou não. Vou verificar aqui e respondo se funcionou! :D

E não funcionou com presenceOfElementLocated()

public ProductPage isNoErrorPresent(){
        waitExplict(1000);
        waitForElementPageToLoad(not(presenceOfElementLocated(By.cssSelector(".alert-danger"))));
        return this;
    }

org.openqa.selenium.TimeoutException: Expected condition failed: waiting for condition to not be valid: presence of element located by: By.cssSelector: .alert-danger (tried for 5 second(s) with 500 MILLISECONDS interval)

@Bruno-Fernandes Que azar =\

@Bruno-Fernandes
Perguntinha idiota: Uma vez que tu espera a presença do elemento e ele não aparece, não seria de captar o erro mostrando e seguir em frente?
Já tive problemas com esse pensamento e resolvi assumir que se der erro por que o elemento não existe, sigo adiante…
Ponto de cuidado, fazer isso pode deixar o teste “meio duvidoso”…

@Ramses-Saccol-de-Almeida , na verdade eu não espero que ele apareça, eu espero é que ele não apareça. Não sei se estou com um pensamento errado, mas a ideia seria de que o esperado é que o alerta de erro não apareça, mas se ele aparecer, fazer o teste falhar.

Por exemplo, quando eu clico em um link, se qualquer exceção ocorrer na aplicação, ela será disparada neste alerta. Assim um smoke test já é verificar se o alerta existe. Pode ser que a tela tenha sido construída certinho mas o alerta apareceu, entende? Num teste manual, se você ve aquele quadrado vermelho pulando na sua cara você já sabe que algo deu errado, mesmo que o resultado esperado do seu teste tenha funcionado (pensando que o esperado nunca será 100% das possibilidades).

@Bruno-Fernandes Então, posso ter mal me explicado. Mas o que quis dizer é, faz a logica esperando o elemento apareça e se ele vem, trava o teste. Se ele não vem, captura o erro e segue adiante…

Eu sempre tive muito cuidado com esse tipo de lógica, por que pode ser outro erro aparecendo e tu “passa ele” sem notar. Por isso o cuidado em não deixar duvidoso.

Acho que entendi o que quis dizer… preciso de um try catch onde ele vai tentar encontrar o elemento. Se encontrar, faço o teste quebrar. Se não encontrar, apenas sigo com o teste.

Mas não manjo muito ainda de tratamentos de exceção, poderia me ajudar nessa? Ficaria algo como abaixo, mas o que iria no catch?

try {
    waitForElementPageToLoad(presenceOfElementLocated(By.cssSelector(".alert-danger")));
    assert false;
} catch (){}

@Bruno-Fernandes

Pensei em algo…

Scanner prompt = new Scanner(System.in);

catch(ExceptionMarota e)
          {
            System.out.println("** Teoricamente sapohha deu errado**");

            prompt.nextLine();

            continue;

          }

Mas tá bem erradinho esse pensamento. Procura por ErrorCatch no stackoverflow…
O mais “sensato” seria fazer algo assim e guardar numa lista e depois comparar se o erro que tu esperava era o que realmente tu precisava.
Como falei, isso gera um pouco mais de trabalho.
Isso e dar uma olhada na lib que o Stefan passou. Acho mais válido essa lib do que essa GPP que passei (Gambiarra Provisória Permanente) heheh

@Ramses-Saccol-de-Almeida vou dar uma olhada na lib, porque to achando muito POG mesmo esse meu pensamento.
Edit: acho que Condition Evaluation Listener pode me ajudar…

A minha ideia seria fazer mais ou menos o que o TestWatcher faz. Tipo, eu criei uma @Rule pro meu teste, extendendo do TestWatcher que sempre que o teste quebrar (failed()) irá disparar uma mensagem pra um canal do slack. Seria daora se eu pudesse fazer algo mais ou menos assim, mas ao invés de se o teste falhar ser se a mensagenzinha aparecer. Assim, como estaria numa @Rule eu nem precisaria ficar verificando a cada teste.

public class ReportToSlackRule extends TestWatcher {

    @Override
    protected void failed(Throwable e, Description description) {

        if (TestProperties.isSlackAlertOn()) {

            String descricaoErro = String.format("%1.200s", description);

            given()
                    .body(new SlackMessage(":bulb:", TestProperties.getSlackAlertChannel(), "Smoke Tests",
                            "Erro ao executar o teste " + descricaoErro + " <@fernandes.bruno>\n"))
                    .when()
                    .post("https://hooks.slack.com/services/aquiVaiOTokenDoSlack");
        }
    }
}

@Bruno-Fernandes
Faz bem mais sentido. Quando pensei em captar e continuar, ela para depois de correr, eu iria analisar a mensagem de erro e dai validar algo sobre se a mensagem de erro ser diferente da que espero.
Mas como falei, deixa o teste muito duvidoso…Eu fiz em casos extremos e gastei um tempo para cuidar muito erro sem ser o que esperava…Resultado foi que captou muita coisa…
Recomendei mesmo como “quick solution”… Ainda apoio dar uma olhada ali na lib que deve ser bemmmm mais interessante…

@Bruno-Fernandes

Mas aí vc teria que fazer 2 waits: um pra esperar o elemento aparecer e outro pra esperar ele sumir.

Não parece ser algo complicado, já aviso que vocês tão indo pra um caminho complicado demais, não tem necessidade de usar @Rule pra isso. Outra coisa importante: cuidado pra não colocar lógica dentro de testes, testes não devem ter if, try/catch, etc.

Olha o awaitility que te falei cara, se os waits do Selenium WebDriver não funcionarem, cria um wait pelo awaitility. Nele você vai conseguir fazer algo tipo await().until(errorAlert.isDisplayed()) facilmente… você pode criar wait pra qualquer coisa que retorne boolean.

@stefanteixeira disse em Ajuda: como verificar se um elemento apareceu na tela?:

Nele você vai conseguir fazer algo tipo await().until(errorAlert.isDisplayed()) facilmente… você pode criar wait pra qualquer coisa que retorne boolean.

Eu não entendi direito como eu passo algo que seja do tipo Callable<Boolean>, pois se eu passo apenas await().until(errorAlert.isDisplayed()) não funciona…

Callable<Boolean>

alt text

Tu não teria que fazer algo tipo

public boolean estaPresente(Element elementoMaroto){
    try {
        driver.findElement(Element);
        return true;
    }
    catch (ExceptionDoElemento e){
        return false;
    }
}

para poder utilizar esse cara? (posso ter entendido mal ae…)

@Bruno-Fernandes

Tá usando Java 8? Com Java 8 você pode fazer isso:

await().atMost(30, TimeUnit.SECONDS).until(() -> errorAlert.isDisplayed());

Dá uma lida na documentação pra entender melhor como usar, tá tudo muito bem explicado btw: https://github.com/awaitility/awaitility/wiki/Usage

@stefanteixeira , não funcionou… ainda dá erro de NoSuchElementException quando o elemento não aparece na tela. Isso acho que é porque para usar o isDisplayed() é preciso que o elemento exista, mas quando não ocorre erro algum na aplicação, este elemento não irá aparecer. Quando eu trabalhava com testeComplete existiam os métodos exists e visibleOnScreen, um era verdadeiro quando o elemento existia no DOM e outro era verdadeiro quando o elemento existia e estava visível. No selenium não tem nada parecido? Eu precisaria de algo como o exists, mas o isDisplayed() está mais parecido com o visibleOnScreen.

@Bruno-Fernandes dá pra vc ignorar exceptions enquanto estiver esperando: https://github.com/awaitility/awaitility/wiki/Usage#example-9---ignoring-exceptions, só colocar essa exception pra ele ignorar

Tentei trabalhar com o awaitility, mas acho que por falta de conhecimentos da minha parte não consegui. Novamente com uma grande ajuda aqui na empresa consegui fazer algo que funcionou e que é bem o que o @Ramses-Saccol-de-Almeida sugeriu: try/catch

Aqui já temos um método que, a cada página entrada são iniciados os elementos da mesma e verifica se a página foi carregada a partir de uma condição (pageLoadCondition) que existe na classe:

public T navegateToPage(Class<T> clazz) {
        T page = PageFactory.initElements(SeleniumDriver.getDriver(), clazz);
        ExpectedCondition pageLoadCondition = ((GenericPage) page).getPageLoadCondition();
        waitForPageToLoad(pageLoadCondition);
        stopOnAlertDanger(clazz);
        return page;
    }

O que fizemos foi incluir mais uma verificação neste momento, que é o stopOnAlertDanger().

private void stopOnAlertDanger(Class<T> clazz) {

        WebDriver driver = SeleniumDriver.getDriver();
        Wait wait = new FluentWait(driver)
                .withTimeout(1, TimeUnit.SECONDS)
                .pollingEvery(200, TimeUnit.MILLISECONDS);
        By cssSelector = By.cssSelector(".alert-danger");

        try {
            wait.until(presenceOfElementLocated(cssSelector));

            driver.manage().timeouts().implicitlyWait(500, TimeUnit.MILLISECONDS);
            WebElement error = driver.findElement(cssSelector);

            if (error.isDisplayed()) {
                throw new RuntimeException("Alert-danger: Classe " + clazz.getName());
            }

        } catch (NoSuchElementException n) {
        }
    }

Assim, caso o Alerta apareça, será lançada uma exception, mas se não for encontrado, o catch irá capturar o erro e seguir adiante.

Agora sempre que o teste entrar em uma página, automaticamente será verificado se algum erro ocorreu! :D

Problema resolvido! \o/

Log in to reply

Looks like your connection to Agile Testers was lost, please wait while we try to reconnect.