-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Method. ExecuteScript #24
Comments
ExecuteScriptを直接呼び出す際のElementIDへの配慮を加えた『ExecuteScript』です。 TinySeleniumVBA WebDriver.cls ' Execute Script '2021/7/4 add ishi -> 2021/07/20 chg ishi -> 2021/9/30 chg ishi
Public Function ExecuteScript(Script As String, _
Optional Scriptargs As Variant = vbNullString, _
Optional ElementId As String = vbNullString, _
Optional ByVal sessionId As String = vbNullString)
Dim Data As New Dictionary
Data.Add "script", Script
' Set ElementID and Args
Dim ElmData As New Dictionary
Dim args As Variant
If ElementId <> vbNullString Then
ElmData.Add "ELEMENT", ElementId
ElmData.Add ELEMENT_KEY, ElementId
args = Array(ElmData)
If VarType(Scriptargs) = vbVariant + vbArray Then 'Scriptargs is Array
Dim i As Integer
For i = 0 To UBound(Scriptargs)
ReDim Preserve args(i + 1)
args(i + 1) = Scriptargs(i)
Next
Else
args = Array(ElmData, Scriptargs)
End If
Else
args = Array()
End If
Data.Add "args", args
' Set sessionId
If sessionId = vbNullString Then
Data.Add "sessionId", DefaultSessionId
Else
Data.Add "sessionId", sessionId
End If
' Set Method and Path
Dim method As String: method = CMD_W3C_EXECUTE_SCRIPT(0)
Dim path As String: path = CMD_W3C_EXECUTE_SCRIPT(1)
' Set params to path
Dim dataKey As Variant
For Each dataKey In Data
If VarType(Data(dataKey)) = vbString Then
path = Replace(path, "$" + dataKey, Data(dataKey))
End If
Next
' Send request to selenium server
Dim resp As Dictionary
Set resp = SendRequest(method, UrlBase + path, Data)
' Return
If IsNull(resp("value")) Then
ExecuteScript = vbNullString 'Return vbnNullString
ElseIf TypeName(resp("value")) = "Collection" Then
Set ExecuteScript = resp("value")
ElseIf VarType(resp("value")) = vbObject Then
If resp("value").Exists("error") Then
Err.Raise 513, "WebDriver.Execute", JsonConverter.ConvertToJson(resp("value"))
Else
Set ExecuteScript = resp("value")
End If
Else
ExecuteScript = resp("value")
End If
End Function TinySeleniumVBA WebElement.cls ' ExecuteScript '2021/9/30 add ishi
Public Function ExecuteScript(Script As String, _
Optional Scriptargs As Variant = vbNullString)
Driver_.ExecuteScript Script, Scriptargs, ElementId_, SessionId_
End Function |
WebElement.cls の ExecuteScript で戻値への配慮が漏れていましたので見直しました。 TinySeleniumVBA WebElement.cls ' ExecuteScript '2021/9/30 add ishi -> 2021/10/24 chg ishi
Public Function ExecuteScript(Script As String, _
Optional ScriptArgs As Variant = vbNullString)
ExecuteScript = Driver_.ExecuteScript(Script, ScriptArgs, ElementId_, SessionId_)
End Function |
Nice contribution @ezagdd . I think (but am not sure?) that ExecuteScript could be simplified to the following below. Note that I added optional timeout and raise input parameters as in here. The only functional difference that I can see between the version below and yours above, aside from the raise and timeout parameters, is the return type if IsNull(resp("value")) condition of the script is true. This version returns an empty Dictionary and yours a null string.
|
ExecuteScript 関数を入力する際に timeOutms 引数の属性を明らかにする為に Optional ByVal timeOutms As Double と Public Function ExecuteScript(ByVal Script As String, _
Optional ScriptArgs As Variant = vbNullString, _
Optional ByVal ElementId As String = vbNullString, _
Optional ByVal SessionId As String = vbNullString, _
Optional ByVal timeOutms As Double, _
Optional ByVal raise As Boolean = False)
Dim Data As New Dictionary
Dim ElmData As New Dictionary
Dim args As Variant
Dim savtimeOutms As Double
Data.Add "script", Script
' Set ElementID and Args
If ElementId <> vbNullString Then
ElmData.Add "ELEMENT", ElementId
ElmData.Add ELEMENT_KEY, ElementId
args = Array(ElmData)
If VarType(ScriptArgs) = vbVariant + vbArray Then 'Scriptargs is Array
Dim i As Integer
For i = 0 To UBound(ScriptArgs)
ReDim Preserve args(i + 1)
args(i + 1) = ScriptArgs(i)
Next
Else
args = Array(ElmData, ScriptArgs)
End If
Else
args = Array()
End If
Data.Add "args", args
' Set sessionId
If SessionId = vbNullString Then
Data.Add "sessionId", DefaultSessionId
Else
Data.Add "sessionId", SessionId
End If
If timeOutms > 0 And raise Then
savtimeOutms = Me.GetScriptTimeout(SessionId)
If savtimeOutms <> timeOutms Then Me.SetScriptTimeout timeOutms, SessionId
End If
ExecuteScript = Execute(CMD_W3C_EXECUTE_SCRIPT, Data, raise)
If timeOutms > 0 And raise Then
If savtimeOutms <> timeOutms Then Me.SetScriptTimeout savtimeOutms, SessionId
End If
End Function もしも Optional ByVal timeOutms のままなら、timeOutms を Doubleに変換して SetScriptTimeout を呼出す必要があります。 If savtimeOutms <> timeOutms Then Me.SetScriptTimeout cDbl(timeOutms), SessionId Sub TestforScroll()
Dim Driver As WebDriver 'Chrome WebDriver Instance
Dim height As Long
Dim scroll As Long
Set Driver = New WebDriver
Driver.Chrome "chromedriver.exe"
Driver.OpenBrowser
Driver.Navigate "https://www.yahoo.co.jp/"
'Scroll
scroll = 0
Do
scroll = scroll + 4
Driver.ExecuteScript "window.scrollTo(0, " & CStr(scroll) & ");"
'Get Page Height
height = Driver.ExecuteScript("return document.body.scrollHeight")
'Over Page Height ?
If scroll > height Then
Exit Do
End If
Loop
'Top Scroll
Driver.ExecuteScript "window.scrollTo(0, 0);"
Driver.Wait
'Down Scroll
Driver.ExecuteScript "window.scrollTo(0, document.body.scrollHeight);"
Driver.Wait
Driver.CloseBrowser
Driver.Shutdown
Set Driver = Nothing
End Sub |
Thanks @ezagdd! In my version (of your original version!) of ExecuteScript, I don't get the error because I had already changed the SetScriptTimeout's optional millisecond parameters to ByVal instead of the default ByRef as in below:
Also, consider changing millisecond variable type to Long, unless Selenium really does accept fractions of milliseconds... In fact, I went through the entire code base and changed any input parameter that was not typed as Object or Variant (such as strongly-typed Double, Long, Boolean, or String) to ByVal. Sorry I should have mentioned this before.... |
@GCuser99 Thank you! |
@ezagdd Thank you for you kind post.
Will you let me know what do you mean? 投稿ありがとうございます!上記、ElementIDへの配慮というのは具体的にどのような意味でしょうか? |
@uezo さん、具体性のない単語で失礼しました。以下の説明でお判り頂けますか。 変更前(2021/07/20)は、ElementIdを渡していませんでした。 ' Execute Script '2021/7/4 add ishi -> 2021/07/20 chg ishi
Public Function ExecuteScript(Script As String, _
Optional args As Variant, _
Optional ByVal sessionId As String = vbNullString)
Dim Data As New Dictionary
Data.Add "script", Script
If VarType(args) <> vbVariant + vbArray Then 'args is Array
args = Array()
End If
Data.Add "args", args 変更後(2021/9/30)は、引数に ElementId を加え ElmData を組み立てると共に javascript に渡す引数も複数可能な様に ' Execute Script '2021/7/4 add ishi -> 2021/07/20 chg ishi -> 2021/9/30 chg ishi
Public Function ExecuteScript(Script As String, _
Optional Scriptargs As Variant = vbNullString, _
Optional ElementId As String = vbNullString, _
Optional ByVal sessionId As String = vbNullString)
Dim Data As New Dictionary
Data.Add "script", Script
Dim ElmData As New Dictionary
Dim args As Variant
If ElementId <> vbNullString Then
ElmData.Add "ELEMENT", ElementId
ElmData.Add ELEMENT_KEY, ElementId
args = Array(ElmData)
If VarType(Scriptargs) = vbVariant + vbArray Then 'Scriptargs is Array
Dim i As Integer
For i = 0 To UBound(Scriptargs)
ReDim Preserve args(i + 1)
args(i + 1) = Scriptargs(i)
Next
Else
args = Array(ElmData, Scriptargs)
End If
Else
args = Array()
End If
Data.Add "args", args |
@ezagdd Thank you, I understood! You mean when pass the web element as an argument, it can be used in JavaScript as a DOM object like below, right? ExecuteScript("alert(arguments[0].value);", scriptArgs, element) ' for example, element is a textbox -> MessageBox will apear with the value of textbox But, sorry, one more question. When ありがとうございます。本当にいつもありがとうございます。理解できました。引数として要素を渡すと、そのDOMオブジェクトをJavaScriptの中で利用できる、ということであってますでしょうか?たとえばコードのように、テキストボックスを引数として渡せば、 一点だけ追加で教えていただきたいのですが、 |
引数として要素を渡すと、そのDOMオブジェクトを
|
@ezagdd Thank you! If so, how about make Public Function ExecuteScript(ByVal script As String, Optional scriptArguments As Variant = Empty, Optional ByVal sessionId As String = vbNullString)
:
:
Dim args()
If Not IsEmpty(scriptArguments) Then
' Abort if scriptArguments passed but not array
If VarType(scriptArguments) < vbArray Then
Err.Raise 13, "WebDriver.ExecuteScript", "scriptArguments must be array"
End If
Dim i As Integer
For i = 0 To UBound(scriptArguments)
ReDim Preserve args(i)
If TypeName(scriptArguments(i)) = "WebElement" Then
' Convert WebElement to Dictionary that can be handled by WebDriver
Dim elem As New Dictionary
elem.Add "ELEMENT", scriptArguments(i).ElementId_
elem.Add ELEMENT_KEY, scriptArguments(i).ElementId_
Set args(i) = elem
Else
args(i) = scriptArguments(i)
End If
Next
End If
Data.Add "args", args Usage: sargs = Array("liella", searchInput, "aquors")
Driver.ExecuteScript "alert(arguments[0] + ' ' + arguments[1].value + ' ' + arguments[2])", sargs This works on my environment. |
I've just added support for JavaScript. If you have any suggestion for additional specs please create issue or discussion. |
@uezo さん 以下は、 Public Function ExecuteScript(ByVal Script As String, _
Optional scriptArguments As Variant = vbNullString, _
Optional ByVal ElementId As String = vbNullString, _
Optional ByVal sessionId As String = vbNullString, _
Optional ByVal timeOutms, _
Optional ByVal raise As Boolean = True)
Dim Data As New Dictionary
Dim ElmData As New Dictionary
Dim args As Variant
Dim savtimeOutms
Dim i As Integer
Data.Add "script", Script
' Set ElementID and Args
If ElementId <> vbNullString Then
ElmData.Add "ELEMENT", ElementId
ElmData.Add ELEMENT_KEY, ElementId
End If
If VarType(scriptArguments) = vbVariant + vbArray Then 'Scriptargs is Array
args = Array(ElmData)
For i = 0 To UBound(scriptArguments)
ReDim Preserve args(i + 1)
args(i + 1) = scriptArguments(i)
Next i
Else
If Not scriptArguments = vbNullString Then
args = Array(ElmData, scriptArguments)
Else
args = Array(ElmData)
End If
End If
Data.Add "args", args
' Set sessionId
If sessionId = vbNullString Then
Data.Add "sessionId", DefaultSessionId
Else
Data.Add "sessionId", sessionId
End If
If Not IsMissing(timeOutms) Then
savtimeOutms = GetScriptTimeout(sessionId)
If savtimeOutms <> timeOutms Then SetScriptTimeout timeOutms, sessionId
End If
Dim Resp As Dictionary
Set Resp = Execute(CMD_W3C_EXECUTE_SCRIPT, Data, raise)
'trap error if occurs 2022/1/3 ishi
If IsResponseError(Resp) Then
Err.raise 513, "WebDriver.ExecuteScript", GetResponseErrorMessage(Resp)
Else
ExecuteScript = Resp("value")
If Not IsMissing(timeOutms) Then
If savtimeOutms <> timeOutms Then SetScriptTimeout savtimeOutms, sessionId
End If
End If
End Function Option Explicit
Sub TestforExecuteScript()
'==============================================================================
' ExecuteScript Easy Test
'==============================================================================
Dim objDrv As WebDriver
Dim objElm As WebElement
Dim Script As String
Dim height As Long
Dim scroll As Long
Dim ArgsArray As Variant
'Open Browser
Set objDrv = New WebDriver
objDrv.Chrome "chromedriver.exe"
objDrv.OpenBrowser
'Navigate to URL
objDrv.Navigate "https://gigazine.net/"
objDrv.Wait
'Alert (No Args)
objDrv.ExecuteScript "alert('-- Element Focus Scroll --');"
objDrv.Wait
objDrv.AcceptAlert
objDrv.Wait
'Element Focus Scroll (Args is NoArray)
Set objElm = objDrv.FindElement(By.ID, "calendar-datepicker")
objElm.ExecuteScript "arguments[0].focus({'preventScroll': arguments[1]})", 0
objDrv.Wait
'Alert (No Args)
objDrv.ExecuteScript "alert('-- Top Scroll --');"
objDrv.Wait
objDrv.AcceptAlert
objDrv.Wait
'Top Scroll (No Args)
objDrv.ExecuteScript "window.scrollTo(0, 0);"
objDrv.Wait
'Alert (No Args)
objDrv.ExecuteScript "alert('-- Scroll 500 --');"
objDrv.Wait
objDrv.AcceptAlert
objDrv.Wait
'Scroll 500 (Args is Array)
ArgsArray = Array(0, 500)
objDrv.ExecuteScript "window.scrollTo(arguments[1], arguments[2]);", ArgsArray 'Args Array
objDrv.Wait
'Alert (No Args)
objDrv.ExecuteScript "alert('-- Top Scroll --');" 'No Args
objDrv.Wait
objDrv.AcceptAlert
objDrv.Wait
'Top Scroll (No Args)
objDrv.ExecuteScript "window.scrollTo(0, 0);"
objDrv.Wait
'Alert (Args is Array)
ArgsArray = Array("TEST", "Alert", "OK?", "CANCEL?")
objDrv.ExecuteScript "alert('-- ' + arguments[1] + ' -- ' + arguments[2] + ' -- ' + arguments[3] + ' -- ' + arguments[4] + ' --');", ArgsArray 'Args Array
objDrv.Wait
objDrv.AcceptAlert
objDrv.Wait
'Alert (No Args)
objDrv.ExecuteScript "alert('-- Top Scroll --');"
objDrv.Wait
objDrv.AcceptAlert
objDrv.Wait
'Top Scroll (No Args)
objDrv.ExecuteScript "window.scrollTo(0, 0);"
objDrv.Wait
'Alert (No Args)
objDrv.ExecuteScript "alert('-- Get Page Height & Scroll --');"
objDrv.Wait
objDrv.AcceptAlert
objDrv.Wait
'Get Page Height & Scroll (No Args)
height = objDrv.ExecuteScript("return document.body.scrollHeight")
objDrv.ExecuteScript "window.scrollTo(0, " & CStr(height) & ");"
objDrv.Wait
'Close Browser
objDrv.CloseBrowser
objDrv.Shutdown
Set objDrv = Nothing
End Sub |
@uezo さん |
Hi @ezagdd ,
Agree. I recognized it as ToDo.
I couldn't get why you want to separate ElementId from other argument values. And, we must provide the way to set multiple elements as argument.
Will you let me know use cases that require |
@uezo さん
複数のElementを引数として渡すことを想定する限り Set objElm = objDrv.FindElement(By.CssSelector, Selector値)
objElm.ExecuteScript "arguments[0].focus({'preventScroll': arguments[1]})", 1 'No Scroll Set objElm = objDrv.FindElement(By.CssSelector, Selector値)
objElm.ExecuteScript "arguments[0].focus({'preventScroll': arguments[1]})", 0 'Scroll |
Hi @ezagdd , Now I've got understood that this discussion is for adding I want to lower the priority of this issue for the reasons below:
|
ありがとうございます。昨日時点で確認済です。 |
TinySeleniumVBA WebDriver.cls
The text was updated successfully, but these errors were encountered: