florentbr / SeleniumBasic

A Selenium based browser automation framework for VB.Net, VBA and VBScript
BSD 3-Clause "New" or "Revised" License
421 stars 197 forks source link

How do I know if this element is visible? #271

Closed DiQuintino closed 11 months ago

DiQuintino commented 11 months ago

When we search on Bing Maps (https://www.bing.com/maps/directions), when typing a non-existent address number, we receive the message "An exact match was not found. Use [address found] or change the search term ."

How do I identify that this message was displayed and capture the [address found] to a variable?

tried:

vChkError = driver.FindElementByXPath("/html/body/div[2]/div[4]/div[1]/div[1]/div[1]/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div[2]/div/div[3]/div[2]/div[1]/div/ul/li[1]/div/div[2]/text()").Text

and

vChkError = driver.FindElementByXPath("/html/body/div[2]/div[4]/div[1]/div[1]/div[1]/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div[2]/div/div[3]/div[2]/div[1]/div/ul/li[1]/div/div[2]/text()[1]").Text

The element at this page:
An exact match was not found. Use NW 12th St, Miami, FL 33172, United States or change the search term.

the return i want: "NW 12th St, Miami, FL 33172, United States"

TYVM imagem_2023-09-26_121846121

GCuser99 commented 11 months ago

Hi @DiQuintino, you could make it a little easier on potential helpers if you would provide minimal code that replicates the problem fully, so that we don't have to redesign the context ourselves. Thanks!

Mike

DiQuintino commented 11 months ago

Sorry.

sub test()
    Dim driver As New Selenium.ChromeDriver

    vGProfile = CStr(Environ("USERPROFILE") & "\AppData\Local\Google\Chrome\User Data")
    driver.SetProfile vGProfile, True

    vCityOrigem = Plan3.Range("A2").Value
    vEndOrigin = Plan3.Range("A3").Value

    vMaps = "https://www.bing.com/maps/directions"
    driver.Get (vMaps)

    On Error Resume Next
    driver.FindElementByXPath _
    ("/html/body/div[2]/div[4]/div[1]/div[1]/div[1]/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div[2]/div[1]/div[3]/div[2]/div[1]/div/ul/li[1]/div/div[1]/input").SendKeys _
    vEndOrigin '
    driver.FindElementByXPath _
    ("/html/body/div[2]/div[4]/div[1]/div[1]/div[1]/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div[2]/div[1]/div[3]/div[2]/div[1]/div/ul/li[2]/div/div[1]/input").SendKeys _
    vCityOrigin 

    driver.FindElementByXPath("/html/body/div[2]/div[4]/div[1]/div[1]/div[1]/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div[2]/div[1]/div[3]/div[4]/div[1]/div/div[2]/a").Click
    vChkErrorOrigin = driver.FindElementByXPath("/html/body/div[2]/div[4]/div[1]/div[1]/div[1]/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div[2]/div/div[3]/div[2]/div[1]/div/ul/li[1]/div/div[2]/a[2]").Text
    debug.print vChkErrorOrigin 
    driver.Close
    driver.Quit
    End sub
GCuser99 commented 11 months ago

Here you go:

Sub Test()
    Dim driver As New Selenium.ChromeDriver
    Dim by As New Selenium.by
    Dim endPos as Long
    Dim vCityOrigem as String
    Dim vEndOrigin as String
    Dim vChkErrorOrigin as String

    vCityOrigem = "9999401 NW 12th St, Miami, FL 33172, United States"
    vEndOrigin = "Other Place Dr, New Braunfels, TX 78130, United States"

    driver.Get "https://www.bing.com/maps/directions"

    driver.Timeouts.ImplicitWait = 1000

    driver.FindElementByCss("input[placeholder='From']").SendKeys vCityOrigem
    driver.FindElementByCss("input[placeholder='To']").SendKeys vEndOrigin

    If driver.IsElementPresent(by.Class("dirResolve"), 1000) Then
        vChkErrorOrigin = driver.FindElementByClass("dirResolve").WaitDisplayed(, 1000).Text
        endPos = InStr(vChkErrorOrigin, " or change the search term.") - 1
        vChkErrorOrigin = Mid$(vChkErrorOrigin, 35, endPos - 35 + 1)
        Debug.Print vChkErrorOrigin
    End If

    driver.Close
    driver.Quit
End Sub
DiQuintino commented 11 months ago

Works perfectly tyvm

DiQuintino commented 11 months ago

Sorry, now i have other situation:

I got the error at "if" statement when: Both address ("from"and "to") are correct 2) if only the "to" field are incorrect.

Ty in advance.

GCuser99 commented 11 months ago

Oops, the "dirResolve" element is always present, but we want to inspect after it has been displayed. Maybe this will work...even though a better solution would be to find a way to get rid of the explicit wait but I'm too tired...

Private Sub Test()
    Dim driver As New Selenium.ChromeDriver
    Dim by As New Selenium.by
    Dim endPos
    Dim elem As Selenium.WebElement

    'vCityOrigem = "NW 12th St, Miami, FL 33172"
    vCityOrigem = "9999401 NW 12th St, Miami, FL 33172, United States"
    vEndOrigin = "Other Place Dr, New Braunfels, TX 78130, United States"

    driver.Get "https://www.bing.com/maps/directions"

    driver.Timeouts.ImplicitWait = 1000

    driver.FindElementByCss("input[placeholder='From']").SendKeys vCityOrigem
    driver.FindElementByCss("input[placeholder='To']").SendKeys vEndOrigin

    Set elem = driver.FindElementByClass("dirResolve")

    driver.Wait 1000 '<-- experiment with this delay

    If elem.IsDisplayed Then
        vChkErrorOrigin = elem.Text
        endPos = InStr(vChkErrorOrigin, " or change the search term.") - 1
        vChkErrorOrigin = Mid$(vChkErrorOrigin, 35, endPos - 35 + 1)
        Debug.Print vChkErrorOrigin
    End If

    driver.Close
    driver.Quit
End Sub
DiQuintino commented 11 months ago

The wait ure talking to is to loading the address or the element "dirResolve"? For the address, u can force by clicking at search buttom:

' driver.FindElementByCss("a[data-tag='dirBtnGo']").Click

I did this changes and it works considering only the "From" field. But it continues to fail to fetch the address suggestion when the error (and the suggestion) is in the "To" field:

Private Sub Test()
    Dim driver As New Selenium.ChromeDriver
    Dim by As New Selenium.by
    Dim endPos
    Dim elem As Selenium.WebElement

    'vCityOrigem = "NW 12th St, Miami, FL 33172"
    vCityOrigem = "999999 NW 12th St, Miami, FL 33172, United States"
    vEndOrigin = "Other Place Dr, New Braunfels, TX 78130, United States"

    driver.Get "https://www.bing.com/maps/directions"

    driver.Timeouts.ImplicitWait = 1000

    driver.FindElementByCss("input[placeholder='De']").SendKeys vCityOrigem
    driver.FindElementByCss("input[placeholder='Para']").SendKeys vEndOrigin
    driver.FindElementByCss("a[data-tag='dirBtnGo']").Click 'forcing load

    driver.Wait 5000 '<-- tried higher too and change the position

    Set elem = driver.FindElementByClass("dirResolve")

    If elem.IsPresent Then
        vChkErrorOrigin = elem.Text
        If Len(vChkErrorOrigin) > 1 Then '<--- Check if is empty (ok, IsEmpty must works too)
            endPos = InStr(vChkErrorOrigin, " or change the search term.") - 1
            vChkErrorOrigin = Mid$(vChkErrorOrigin, 35, endPos - 35 + 1)
            Debug.Print Now(); vChkErrorOrigin
        End If
    End If

    driver.Close
    driver.Quit
End Sub

thx again

DiQuintino commented 11 months ago

Found the problem but idk how to get it.

Exists more than one "dirResolve", for each address put there. So, i need to check the second ocurrence (since im waiting for the first "to" address

DiQuintino commented 11 months ago

Not a elegant way but i got the value :}

Private Sub Test()
    Dim driver As New Selenium.ChromeDriver
    Dim by As New Selenium.by
    Dim endPos
    Dim elem As Selenium.WebElement

    'vCityOrigem = "NW 12th St, Miami, FL 33172"
    vCityOrigem = "NW 12th St, Miami, FL 33172, United States"
    vEndOrigin = "999999999  Other Place Dr, New Braunfels, TX 78130, United States"

    driver.Get "https://www.bing.com/maps/directions"

    driver.Timeouts.ImplicitWait = 1000

    driver.FindElementByCss("input[placeholder='De']").SendKeys vCityOrigem
    driver.FindElementByCss("input[placeholder='Para']").SendKeys vEndOrigin
    driver.FindElementByCss("a[data-tag='dirBtnGo']").Click 'forcing load

    driver.Wait 1000 '<-- tried higher too and change the position

'    Set elem = driver.FindElementByClass("dirResolve")
    Set elem1 = driver.FindElementByXPath("//*[@id='directionsPanelRoot']/div/div[3]/div[2]/div[1]/div/ul/li[1]/div/div[2]")

    If elem1.IsDisplayed Then
        vChkErrorOrigin = elem1.Text
        If Len(vChkErrorOrigin) > 1 Then
        endPos = InStr(vChkErrorOrigin, " or change the search term.") - 1
        vChkErrorOrigin = Mid$(vChkErrorOrigin, 35, endPos - 35 + 1)
            Debug.Print "vChkErrorOrigin: "; Now(); vChkErrorOrigin
        End If
    End If

    Set elem2 = driver.FindElementByXPath("//*[@id='directionsPanelRoot']/div/div[3]/div[2]/div[1]/div/ul/li[2]/div/div[2]")

    If elem2.IsDisplayed Then
        vChkErrorOrigin = elem2.Text
        If Len(vChkErrorOrigin) > 1 Then
           endPos = InStr(vChkErrorOrigin, " or change the search term.") - 1
           vChkErrorOrigin2 = Mid$(vChkErrorOrigin, 35, endPos - 35 + 1)
            Debug.Print "vChkErrorOrigin2: "; Now(); vChkErrorOrigin2
        End If
    End If

    driver.Close
    driver.Quit
End Sub
GCuser99 commented 11 months ago

Yes that's right - I found same solution:

Private Sub Test()
    Dim driver As New Selenium.ChromeDriver
    Dim by As New Selenium.by
    Dim endPos As Long
    Dim elem As Selenium.WebElement
    Dim elems As Selenium.WebElements

    'vCityOrigem = "NW 12th St, Miami, FL 33172"
    vCityOrigem = "999999 NW 12th St, Miami, FL 33172, United States"
    vEndOrigin = "Other Place Dr, New Braunfels, TX 78130, United States"

    driver.Get "https://www.bing.com/maps/directions"

    driver.Timeouts.ImplicitWait = 1000

    driver.FindElementByCss("input[placeholder='From']").SendKeys vCityOrigem
    driver.FindElementByCss("input[placeholder='To']").SendKeys vEndOrigin
    driver.FindElementByCss("a[data-tag='dirBtnGo']").Click 'forcing load

    driver.Wait 3000

    'there are two dirResolve elements and they are both always present
    Set elems = driver.FindElementsByClass("dirResolve")

    'find out which (if any) dirResolve elem is displayed, and process its text
    For Each elem In elems
        If elem.IsDisplayed Then
            vChkErrorOrigin = elem.Text
            endPos = InStr(vChkErrorOrigin, " or change the search term.") - 1
            vChkErrorOrigin = Mid$(vChkErrorOrigin, 35, endPos - 35 + 1)
            Debug.Print Now(), vChkErrorOrigin
        End If
    Next elem

    driver.Close
    driver.Quit
End Sub
DiQuintino commented 11 months ago

Ur solution is more elegant than mine, as always.

TYVM Mike @GCuser99

GCuser99 commented 11 months ago

Well, your way might be better if you need to take some action depending on whether the suggested correction is from the start or end location...

Set elemFromCorrected = driver.FindElementByCss("div.dirWaypoints>div>ul>li:nth-child(1)>div>div.dirResolve")
Set elemToCorrected = driver.FindElementByCss("div.dirWaypoints>div>ul>li:nth-child(2)>div>div.dirResolve")
DiQuintino commented 11 months ago

I did some changes and instead of get the sugested addres and fill the field, i just click at the sugestion :)

Sub sConsultaBing()

    With Plan3
        vULPlan3 = .Range("B" & Rows.Count).End(xlUp).Row
        .Range("B2:C" & vULPlan3).ClearContents
        .Range("B2:C" & vULPlan3).Font.Color = vbBlack
        .Range("B2:C" & vULPlan3).Font.Bold = False
    End With

    Dim driver As New Selenium.ChromeDriver
    Dim by As New Selenium.by
    Dim endPos As Long
    Dim vCidadeOrigem As String
    Dim vEndOrigem As String
    Dim vChkErroOrigem As String

    Call sINI

    vGProfile = CStr(Environ("USERPROFILE") & "\AppData\Local\Google\Chrome\User Data")
    driver.SetProfile vGProfile, True

    vCidadeOrigem = Plan3.Range("A5").Value & " - MG"
    vEndOrigem = Plan3.Range("A6").Value

    'Conta as linhas de Endereço dos POCs
    vULPlan1 = Plan1.Range("H" & Rows.Count).End(xlUp).Row
    'Define a URL do Maps
    vMaps = "https://www.bing.com/maps/directions"

    ''    'Não exibir o Chrome
'    driver.AddArgument ("--headless=new")

    'Abre o site
    driver.Get (vMaps)

    driver.Timeouts.ImplicitWait = 3000

    driver.FindElementByCss("input[placeholder='De']").SendKeys vEndOrigem
    driver.FindElementByCss("input[placeholder='Para']").SendKeys vCidadeOrigem
'    driver.FindElementByCss("a[data-tag='dirBtnGo']").Click

    driver.Wait 3000

'from field (dirResolve)
Set vErroOrigem = driver.FindElementByXPath("//*[@id='directionsPanelRoot']/div/div[3]/div[2]/div[1]/div/ul/li[1]/div/div[2]")

    If vErroOrigem.IsDisplayed Then
        driver.Wait 1000
       'Click at sugestion
        driver.FindElementByXPath("//*[@id='directionsPanelRoot']/div/div[3]/div[2]/div[1]/div/ul/li[1]/div/div[2]/a[2]").Click
'       click search
        driver.FindElementByCss("a[data-tag='dirBtnGo']").Click
    End If

    Call sFIM
Exit  sub
GCuser99 commented 11 months ago

Good to hear that you have it figured out.

Your postings of code are hard to read. If you want to improve that, open and close a block of code using three "back ticks" (upper left on standard keyboard). You can add the "vba" after the opening back ticks if you want Github to colorize the code:

```vba
Dim mycode as String
mycode="improved readability"
DiQuintino commented 11 months ago

fixed, ty again :D