-
Notifications
You must be signed in to change notification settings - Fork 18
Home
Welcome to the SeleniumVBA Wiki! Suggestions and contributions are welcome in this discussion here.
- Getting Started
- Session Flow Basics - An example
- Object Instantiation
- Element Selection Methods
- Working with Capabilities
- Information Capture
- Working with Action Chains
- Navigating Shadow Roots
- Advanced Customization
- Explicit and Implicit Waits
- Executing JavaScript Code
- File/Folder Path Specification
- Managing JavaScript Alerts
- Browser-Keyboard Interaction with SendKeys
- Managing Windows
Appendix: Object Model Overview
SeleniumVBA can be used to automate the Google Chrome, MS Edge, and Firefox web browsers in MS Windows from MS Office applications. Three components are needed to use SeleniumVBA – one of the automation solutions from this GitHub repository, at least one installed web browser, and the Selenium WebDriver(s) corresponding to the installed web browser(s).
SeleniumVBA Add-in. To install SeleniumVBA, download SeleniumVBA.xlam Excel Add-in (or SeleniumVBA.accdb if using with MS Access) to a local file path. That is all that is required from the SeleniumVBA GitHub repository. The Add-in consists of the SeleniumVBA code base of class and standard modules, and a set of test* modules that you can run to learn how to use SeleniumVBA through example.
SeleniumVBA DLL. Another automation solution is to install the SeleniumVBA ActiveX DLL. This DLL must be installed and registered using the SeleniumVBADLLSetup.exe Inno setup program. Once installed, the DLL can be referenced by your VBA projects in MS Excel and MS Access to expose the SeleniumVBA object model without having to manage the SeleniumVBA source code. The ActiveX DLL was compiled using the (currently in Beta) twinBASIC compiler.
Web Browser(s). One or more of the web browser(s) that you intend to automate must be installed and working on your Windows system.
Selenium WebDriver(s). The SeleniumVBA wrapper is an HTTP client of the Selenium WebDriver server. SeleniumVBA sends commands issued by the user programmatically to a local WebDriver server, which in turn controls the corresponding web browser.
As such, SeleniumVBA requires that Selenium WebDriver(s) corresponding to each web browser that you intend to automate, be installed on your system in a location known to SeleniumVBA. While not necessary (see Auto-Check-and-Install below), WebDrivers for each browser can be downloaded from the following sources:
WebDriver-Browser Version Alignment. It is important that a compatible version of the WebDriver be installed for any given browser version using compatibility rules published by each WebDriver supplier. For Chrome and Edge browsers for example, at least the first part of the version number must match between WebDriver and browser. Since the installed browser version changes frequently, so must the installed WebDriver change to stay compatible with the browser for automation to work properly.
Auto-Check-and-Install. The installation of the Selenium WebDriver(s) can be performed manually by downloading from their respective sources above, or more conveniently, you can let SeleniumVBA manage version alignment automatically, in the background, which is the default mode of operation out-of-the-box.
To let SeleniumVBA manage driver/browser version alignment automatically, open SeleniumVBA.xlam (or SeleniumVBA.accdb) and run some of the subroutines in the "test" Standard modules. When any one of the StartChrome
, StartEdge
, or StartFirefox
methods are invoked, SeleniumVBA will detect if the Selenium WebDriver exists and is up-to-date, and if not, will automatically download the appropriate WebDriver to the default location (currently your Windows Download folder but that is easily configurable).
If you wish to turn off the auto-check-and-align feature and manage browser/driver version alignment manually (not recommended) then see the Advanced Customization topic in this Wiki.
Also, you can programmatically invoke WebDriver updates via the WebDriverManager
class, as shown in the following code snippet.
Dim mngr As New WebDriverManager
'mngr.DefaultDriverFolder = [your binary folder path here] 'defaults to Downloads dir
Debug.Print mngr.AlignEdgeDriverWithBrowser()
Debug.Print mngr.AlignChromeDriverWithBrowser()
Debug.Print mngr.AlignFirefoxDriverWithBrowser()
Integrating SeleniumVBA Into Your Projects. SeleniumVBA can be integrated into your VBA code projects in two ways.
The easiest method is to reference the Add-in or DLL externally from your VBA code project. This might be of use if you want a convenient means of updating SeleniumVBA with newer versions of the Add-in (or DLL) file, which can easily be replaced each time a newer version is available. Follow these steps to reference the Add-in from your code project:
- Open the VBA macro project that will reference and use the Add-in.
- In the Visual Basic for Applications window, select a code module, then click on Tools tab, References.
- On the References Dialog, click on Browse, select Microsoft Excel Files as File Type, then browse to the Add-in folder location and select the Add-in and close the dialog.
- If you are using the DLL solution, then just scroll down the References Dialog list to find SeleniumVBA, click on the checkbox to select, and close the dialog.
- Save the Excel macro project.
The second method is to port the SeleniumVBA classes directly into your own code. To accomplish this, do not try to copy-paste the SeleniumVBA modules into your project because there are hidden attributes that are important to proper function of SeleniumVBA that copy-paste will ignore. Instead, from inside your code project, import each of the SeleniumVBA source files from the GitHub src directory. Once all of the modules have been imported, then make sure to set the list of the needed references found here. Consider that this method has the disadvantage that you will have to re-import the source files each time a new version of SeleniumVBA available, should you want to use the newer version.
The code below illustrates the typical 6-step basic flow for a SeleniumVBA session.
Dim driver As New WebDriver
Dim keys As New WebKeyboard
driver.StartChrome 'Step 1 - initializes driver to work for Chrome
driver.OpenBrowser 'Step 2 - opens a "blank" browser session
driver.NavigateTo "https://www.google.com/" 'Step 3 - navigates to a web page
driver.Wait 1000
'Step 4 - find and manipulate web page elements
driver.FindElement(by.Name, "q").SendKeys "SeleniumVBA" & keys.ReturnKey
driver.Wait 1000
driver.CloseBrowser 'Step 5 - close the browser session
driver.Shutdown 'Step 6 - shuts the driver down
Each basic flow step is briefly described below.
Step 1 - Driver Initialization. This required step starts one of the Selenium WebDrivers that will be used for automation. Use one of the methods StartChrome
, StartEdge
, or StartFirefox
depending on which browser will be automated.
Step 2 - Open Browser Session. This opens a browser session of the type indicated by the selected Start*
method. Only one browser session per initialized WebDriver
is allowed. Multiple sessions can be actuated by instantiating multiple WebDriver
objects. The OpenBrowser
method optionally takes a WebCapabilities
object as input that can be used to for advanced settings for browser behaviors if desired, such as running in headless (invisible) mode. A blank browser web page is generated as a result of this command.
Step 3 - Web Page Navigation. Opens the given URL in your web browser. The WebDriver
will wait until the page has loaded before returning control to your calling procedure. However, be aware that web page scripts and dynamic content may continue past the point that navigation has completed, so it is usually good practice to use an explicit or implicit Wait method of the WebDriver
class to make sure the web page has reached stasis before interacting with it.
Step 4 - Element Interaction. There are many methods available in SeleniumVBA that can be used to interact with the elements on the page. The interaction will usually start with one of the element(s) "Find*" methods to locate the element(s), followed by actions such as with the Click
, SendKeys
, GetText
, GetAttribute
, or TableToArray
methods.
Step 5 - Close Browser Session. The CloseBrowser
method of the WebDriver
class ends the browser session that was started with the OpenBrowser
method. Note that the driver is still running, and OpenBrowser
can be invoked again if needed.
Step 6 - Driver Shutdown. The Shutdown
method of the WebDriver
class terminates the driver and performs cleanup.
Object instantiation works differently depending on whether you reference SeleniumVBA externally from another code project, or you integrate the SeleniumVBA source within your own code project. If using SeleniumVBA classes that reside within the same project, or using a reference to the SeleniumVBA DLL, then the usual rules for VBA object instantiation apply:
Dim driver As WebDriver
Set driver = New WebDriver
However, if referencing the SeleniumVBA Add-in externally, the following syntax must be used:
Dim driver As SeleniumVBA.WebDriver
Set driver = SeleniumVBA.New_WebDriver
There are several SeleniumVBA classes that cannot be instantiated in the ways shown above because they require browser-specific initializations. They include WebCapabilities
, WebCookies
, WebActionChain
, WebAlert
, WebElement
, WebShadowRoot
, WebWindow
, and WebWindows
classes. These must be initialized after the Start*
method has been called.
The table below summarizes the means of instantiation for all SeleniumVBA classes:
Class | Creatable with New? | Predeclared? | Example Instantiation |
---|---|---|---|
WebActionChain | No | No | Dim actions as WebActionChain Set actions = driver.ActionChain |
WebAlert | No | No | Dim alert as WebAlert Set alert = driver.SwitchToAlert |
WebCapabilities | No | No | Dim caps as WebCapabilities Set caps = driver.CreateCapabilities |
WebCookie | Yes | No | Dim cookie as New WebCookie |
WebCookies | No | No | Dim cookies as WebCookies Set cookies = driver.GetAllCookies |
WebDriver | Yes | No | Dim driver as New WebDriver |
WebDriverManager | Yes | No | Dim manager as New WebDriverManager |
WebElement | No | No | Dim elem as WebElement Set elem = driver.FindElementByName("Jose") |
WebElements | Yes | No | Dim element as New WebElements |
WebJsonConverter | Yes | Yes | WebJsonConverter.ConvertToJson(dictionary) |
WebKeyboard | Yes | Yes | WebKeyboard.CtrlKey |
WebPrintSettings | Yes | No | Dim settings as New WebPrintSettings |
WebShadowRoot | No | No | Dim shadowRoot as WebShadowRoot Set shadowRoot = driver.GetShadowRoot |
WebWindow | No | No | Dim window as WebWindow Set window = driver.ActiveWindow |
WebWindows | No | No | Dim windows as WebWindows Set windows = driver.Windows |
SeleniumVBA provides eight strategies to "find" or locate elements on a page for further interaction. Element location is performed via the FindElement
, FindElements
, and IsPresent methods of the WebDriver
and WebElement
classes.
Set elementFound = driver.FindElement(By.Name, "find me")
Set elementsFound = driver.FindElements(By.TagName, "div")
The locator object "By" has eight strategy properties. A brief description of each follows.
Locating by ID. Use this locator when the id attribute of an element is known.
Locating by TagName. Use this locator when you want to locate an element by tag name.
Locating by ClassName. Use this locator when you want to locate an element by class name.
Locating by Name. Use this locator when the name attribute of an element is known.
Locating by CssSelector. Use this locator when you want to locate an element using the very powerful CSS selector syntax. For example, this location strategy can be used to find an element with a specific attribute value.
Locating by XPath. XPath is used for locating elements in the HTML DOM tree path. XPath supports the methods of locating by id or name attributes and extends them by allowing, for example, the location of the second table relative to a parent element. One of the advantages for using XPath is when an element that you want to locate does not have a name or id attribute. You can use XPath to either locate the element in absolute terms, or relative to an element that does have an id or name attribute. XPath locators can also be used to specify elements via attributes other than id and name.
Note that absolute XPaths specify the location of all elements from the HTML document root to the target element. If only a small change to a web page structure is made, then the absolute path will need to change. Therefore, using absolute paths is not a very robust location strategy. By finding a nearby element in the tree with an id or name attribute (ideally a parent element) you can locate your target element based on a path relative to the nearby element.
If using the XPath selector in the context of searching for a WebElement(s)
object starting from a specific WebElement
node, be sure to use the "./" or ".//" syntax for an absolute or relative path respectively:
Set elem = driver.FindElement(By.ID, "ascendant")
'this will start search from the ascendant node
Debug.Print elem.IsPresent(By.XPath, ".//div[@id = 'descendant']"
'without the period (.), this will start search from the document root, not the ascendant node!!
Debug.Print elem.IsPresent(By.XPath, "//div[@id = 'descendant']"
Also, be aware that the XPath locator cannot be used to locate elements belonging to a WebShadowRoot object. To find elements of a WebShadowRoot
, use another locator strategy such as the cssSelector (for more info see Navigating Shadow Roots).
Locating by LinkText. Use this locator when you know the link text used within an anchor tag. Note that this locator is case-sensitive.
Locating by PartialLinkText. Use this locator when you know any portion of the link text used within an anchor tag. Note that this locator is case-sensitive.
Selenium Capabilities are a series of key/value pairs that can be used to customize or configure a browser session. Due to the advanced nature of this topic, you are encouraged to review the following links for more information.
Chrome: https://chromedriver.chromium.org/capabilities
Edge: https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options
Firefox: https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
The WebCapabilities
object should be instantiated using WebDriver.CreateCapabilities
after the StartEdge
, StartChrome
, or StartFirefox
methods, and then passed to OpenBrowser
method, as in following example...
Dim caps as WebCapabilities, driver as new WebDriver
driver.StartEdge
Set caps = driver.CreateCapabilities
caps.RunInvisble
driver.OpenBrowser caps
The WebCapabilities
class has general methods to set and manage capabilities, options, preferences, and arguments. In addition, it includes functionality for several common settings such as to specify download behaviors, running the browser in headless (invisible) mode, and running in private (incognito) mode. One special feature of the class is a method to load a set of capabilities from file (see LoadFromFile
method), which allows for a high degree of browser session customization.
The OpenBrowser
method of WebDriver
class has optional input capabilities parameters that can be used to avoid directly instantiating the WebCapabilities
object for a few of the most common capabilities:
-
invisible
: If set to True (default=False), adds the--headless
argument to the default capabilities -
incognito
: If set to True (default=False), adds the--Incognito
argument to the default capabilities -
capabilitiesFilePath
: If specified, initializes the defaultWebCapabilities
object with a previously saved json file (see:SaveToFile
method of theWebCapabilities
class, and example in test_Capabilities module)
To automatically load a set of default capabilities each time the OpenBrowser
method is called, see the Advanced Customization topic in this Wiki.
SeleniumVBA has methods to facilitate information/data capture from web pages. Several of the most important methods are described below.
The Get Methods. The GetAttribute
, GetText
, GetInnerHTML
, GetOuterHTML
methods of the WebDriver
class retrieve info from web page elements. An example using the GetText
method to extract information from a table:
Debug.Print driver.FindElement(By.XPath, "//table[@id='mytable']/tbody/tr[" & irow & "]/td[" & icol & "]").GetText
File Downloading. Use the SetDownloadPrefs
method of the WebCapabilities
class to set the default file path for downloads. Additionally, the DeleteFiles
method of the WebDriver
class is useful for deleting legacy files prior to downloading, and the WaitForDownload
method for ensuring the download is complete before closing the browser. The following code illustrates using these methods to download a pdf file from this GitHub repository.
Dim driver As New WebDriver
Dim caps As WebCapabilities
driver.StartChrome
'set the directory path for saving download to
Set caps = driver.CreateCapabilities
caps.SetDownloadPrefs downloadFolderPath:=".\", promptForDownload:=False, disablePDFViewer:=True
driver.OpenBrowser caps
'delete legacy copy if it exists
driver.DeleteFiles ".\test.pdf"
driver.NavigateTo "https://github.com/GCuser99/SeleniumVBA/raw/main/dev/test_files/test.pdf"
'wait until the download is complete before closing browser
driver.WaitForDownload ".\test.pdf"
driver.CloseBrowser
driver.Shutdown
The TableToArray Method. This method of the WebDriver
class efficiently parses a table or tbody element into 2-D variant array holding the cell text values. Use the optional ignoreHeader
(default=False), ignoreFooter
(default=False), and createSpanData
(default=True) input parameters to customize the result. The createSpanData
will fill in (replicate) missing spanner data resulting in an orderly 2-D data matrix. The TableToArray
also handles nested tables. Example usage:
tableArray = driver.FindElement(By.ID, "mytable").TableToArray()
See the test_Tables module for more complete examples.
PageTo Methods. HTML, XML, and Json Web page sources can be saved in their respective native document formats or directly to file for further analysis, parsing, or for archive. For HTML documents, an advanced option to "sanitize" the source to disable dynamic "online" content without changing the DOM tree structure may be useful. Below is an example:
Dim driver As New WebDriver
Dim htmlDoc As HTMLDocument
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "https://it.wikipedia.org/wiki/Pagina_principale"
driver.Wait 1000
'use DOM to parse htmlDocument here if desired....
'html DOM can much faster than Selenium if complicated parse is needed
'note that santization leaves DOM tree intact
Set htmlDoc = driver.PageToHTMLDoc(sanitize:=True)
Debug.Print htmlDoc.body.ChildNodes.Length
'save sanitized page to html file
driver.PageToHTMLFile "source_sanitized.html", sanitize:=True
'now reload the sanitized source for doing more stuff...
'this loads much faster because santization disables "online" dynamic elements
driver.NavigateToFile "source_sanitized.html"
driver.Wait 1000
The DownloadResource Method. This method can be used to download an element's attribute reference such as an image "src" attribute to a local file. You can either specify a file path to save the resource to or specify a folder path and the resulting file will inherit the resource's file name.
driver.NavigateTo "https://github.com/GCuser99/SeleniumVBA/wiki"
driver.Wait 1000
Set element = driver.FindElement(By.CssSelector, "img[alt='SeleniumVBA'")
'if a folder path is specified for fileOrFolderPath, then the saved file inherits the name of the source
element.DownloadResource srcAttribute:="src", fileOrFolderPath:=".\"
An Action Chain allows you to automate low-level interactions with a web page such as mouse movements, mouse button actions, keypress, and context menu interactions. This is useful for performing more complex actions like hover-over and drag-and-drop.
Action Chains are implemented with the WebActionChain
class, which stores user-specified operations in a queue and then when the Perform
method is called, performs the queued operations. There are two ways to build and execute an Action Chain:
Method 1 - Using the WebActionChain
Object:
Dim actions As WebActionChain
Set elemEmail = driver.FindElement(by.Name, "email")
Set actions = driver.ActionChain 'this is the way to instantiate a WebActionChain object
'Note that a chain can consist of as many code lines, and as many actions per code line as needed
actions.MoveToElement(elemEmail).Click
actions.SendKeys("[email protected]").Perform 'the chain must end in Perform to be executed
Method 2 - Using WebDriver
Class's ActionChain
Method:
Set elemEmail = driver.FindElement(by.Name, "email")
'Note that entire chain + perform must be on one line for this method to work
driver.ActionChain.MoveToElement(elemEmail).Click.SendKeys("[email protected]").Perform
There are over 25 WebActionChain
methods that can be used for complex keyboard and mouse interaction sequences. Use the WebKeyboard
class for advanced key press support.
A shadow root is a DOM subtree that is rendered separately from a document's main DOM tree. Unfortunately, the locator strategies above will not function in the usual way to "find" an element within one of these shadow root subtrees. To expose the shadow root's DOM subtree, use the FindElement
method to return the WebElement
that hosts the target shadow root, and then call the GetShadowRoot
method from the host element to return the WebShadowRoot
object. The WebShadowRoot
class supports the FindElement
and FindElements
methods for locating elements within the shadow root.
To illustrate with an example, below we find the Chrome browser's version number on the Chrome Settings page. Examination of the full XPath to the div element that holds the version text reveals the shadow-root nodes indicated as "//" below, and the elements that host each of the shadow roots having the tag names "settings-ui", "settings-main", and "settings-about-page":
/html/body/settings-ui//div[2]/settings-main//settings-about-page//settings-section[1]/div[2]/div[2]/div[2]
To traverse that path using SeleniumVBA we successively find each host element, get the associated shadow root, and so on until we locate the target element:
Dim driver As New WebDriver
Dim shadowHost1 As WebElement
Dim shadowHost2 As WebElement
Dim shadowHost3 As WebElement
Dim targetElement As WebElement
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "chrome://settings/help"
driver.Wait 1000
'below navigates through three nested shadow roots to find the target
Set shadowHost1 = driver.FindElement(By.TagName, "settings-ui")
Set shadowHost2 = shadowHost1.GetShadowRoot.FindElement(By.TagName, "settings-main")
Set shadowHost3 = shadowHost2.GetShadowRoot.FindElement(By.TagName, "settings-about-page")
Set targetElement = shadowHost3.GetShadowRoot.FindElement(By.ClassName, "secondary")
Debug.Print targetElement.GetText
driver.CloseBrowser
driver.Shutdown
One important limitation to be aware of is that the XPath selector is not supported by the WebShadowRoot
object. Use one of the other selectors (such as CSS selector) to find elements under a shadow root.
Some program defaults in SeleniumVBA can be customized via a settings file and used by the program each time SeleniumVBA runs. These custom defaults include setting browser-specific capabilities, turning off the auto-check-and-update-driver feature, and changing the default location of the Selenium WebDrivers.
In addition to managing program defaults, new WebDriver
functionality can be created for as-yet unwrapped commands using the ExececuteCmd
and/or ExecuteCDP
methods of the WebDriver class. The ExecuteCDP
exposes the Chrome DevTools Protocol, providing a low-level interface for interacting with the Chrome\Edge browsers.
SeleniumVBA Ini file. To customize these defaults, a file called SeleniumVBA.ini must be located in the same folder as the file that holds the SeleniumVBA code library (the Excel Add-in, the Access db, or the ActiveX DLL). An example ini file can be copied from the dist folder of the repository. Additionally, the ini file can be created programmatically using the CreateSettingsFile
method of the WebDriver
class.
Using the SeleniumVBA.ini file is optional. If the file is missing SeleniumVBA will use system defaults. Also, if an entry in the file is commented out or missing, or if an entry value is missing, SeleniumVBA will ignore the entry and use the corresponding system default. All path-type entries below recognize the %[Environ]% syntax.
Note on Potential Portability Pitfall. One thing to keep in mind is that if your code's running successfully depends on custom settings from the SeleniumVBA.ini file, and then you port that code to a different system, or share it with someone else, then you must also share the custom settings from the SeleniumVBA.ini file for the code to function as expected.
Below is an example SeleniumVBA.ini:
# This settings file is completely optional. For it to have effect,
# it must be located in the same folder as the SeleniumVBA code
# library, and be named "SeleniumVBA.ini".
# If a value for an entry is not specified, then the system
# default value will be used.
# Note that all path-type entry values recognize the %[Environ]% syntax.
# A few useful Environ values for reference:
# %USERPROFILE%=C:\Users\[user name]
# %APPDATA%=C:\Users\[user name]\AppData\Roaming
# %LOCALAPPDATA%=C:\Users\[user name]\AppData\Local
# %TEMP%=C:\Users\[user name]\AppData\Local\Temp
[GENERAL]
# The driver_location_folder system defaults to Downloads folder.
# The default_io_folder system defaults to the active vba project's
# document location - leave this blank to use default.
# Valid values for command_window_style are vbHide (default),
# vbNormalFocus, vbMinimizedFocus, vbMaximizedFocus, vbNormalNoFocus,
# and vbMinimizedNoFocus.
# The system default values for implicit_wait, page_load_timeout, and
# script_timeout are 0, 300000, and 30000 ms respectively.
driver_location_folder=%USERPROFILE%\Documents\SeleniumVBA\drivers
default_io_folder=
command_window_style=vbHide
implicit_wait=0
page_load_timeout=300000
script_timeout=30000
[AUTO-DRIVER-UPDATE]
# If auto_detect_and_update=True (system default) then everytime
# the WebDriver's Start* method is called, the Selenium driver's
# version is checked against the corresponding browser version.
# If the driver is not compatible with browser, it will be updated.
# min_compatibility_level determines trigger for updating an
# an out-of-date driver. System default is svbaBuildMajor.
# Use svbaMinor for less frequent updating, and svbaExactMatch
# for more frequent updating.
auto_detect_and_update=True
min_compatibility_level=svbaBuildMajor
# Below are browser-specific initializations.
# To automatically initialize a set of capabilities each time the
# OpenBrowser method of WebDriver class is invoked, set the
# preload_capabilities_file_path entry to the path of a valid json
# capabilities file. Note that if preload_capabilities_file_path is
# set to a blank value, or the entry is missing or commented out,
# then this option is ignored. Use the SaveToFile method of the
# WebCapabilities class to save a default set of capabilities
# for pre-loading.
# The system defaults for local_host_port:
# Chrome - 9515, Edge - 9516, Firefox - 4444, IE - 5555
[CHROME]
preload_capabilities_file_path=%USERPROFILE%\Documents\SeleniumVBA\capabilities\chrome.json
local_host_port=9515
[EDGE]
preload_capabilities_file_path=%USERPROFILE%\Documents\SeleniumVBA\capabilities\edge.json
local_host_port=9516
[FIREFOX]
preload_capabilities_file_path=%USERPROFILE%\Documents\SeleniumVBA\capabilities\firefox.json
local_host_port=4444
preload_capabilities_file_path=
local_host_port=5555
[PDF_DEFAULT_PRINT_SETTINGS]
# Valid units values are svbaInches (default) or svbaCentimeters.
# Valid orientation values are svbaPortrait (default) or svbaLandscape.
units=svbaInches
page_height=11
page_width=8.5
margin_bottom=.393701
margin_top=.393701
margin_right=.393701
margin_left=.393701
background=False
orientation=svbaPortrait
print_scale=1.0
shrink_to_fit=True
Pre-loading Capabilities. Under each of the browser-specific sections in the settings file, there is an entry called preload_capabilities_file_path. If the entry points to an actual json file path, then it tells SeleniumVBA to load a set of capabilities from that file each time the OpenBrowser
method is started, without you having to explicitly initialize and pass a WebCapabilities
object. The json file(s) must conform to the Selenium capabilities format. One easy way to create the capabilities json file is to use the SaveToFile
method of the WebCapabilities
class as in the code snippet below:
Dim driver As New WebDriver
Dim caps As WebCapabilities
driver.StartFirefox
Set caps = driver.CreateCapabilities
'set capabilities that you want to load each time OpenBrowser is started
caps.RunIncognito
caps.SetDownloadPrefs downloadFolderPath:=".\", promptForDownload:=False, disablePDFViewer:=True
caps.SetAcceptInsecureCerts
'save capabilities to a json file
caps.SaveToFile "%USERPROFILE%\Documents\SeleniumVBA\capabilities\firefox.json"
driver.Wait 1000
driver.Shutdown
Keep in mind that if you use this method to pre-load capabilities, any capabilities that you do explicitly pass to the OpenBrowser
method via the WebCapabilities
object will be merged with the capabilities that are preloaded by default. If you would like to ignore the pre-loaded capabilities, then you can set the initializeFromSettingsFile
parameter to False as in the following:
'initializeFromSettingsFile:=False tells SeleniumVBA to not use
'the preloaded capabilities from settings file (default=True)
Set caps = driver.CreateCapabilities(initializeFromSettingsFile:=False)
caps.RunIncognito True
caps.RunInvisible True
driver.OpenBrowser caps 'here is where caps is passed to driver
The ExecuteCmd Method. This method of the WebDriver class can be used to test HTTP endpoint commands that have not been wrapped by SeleniumVBA. Each command consists of the command path (such as /session/$sessionId/moz/screenshot/full
) and an input parameter Dictionary object (if inputs are required). Input arguments in the actual command path (for example "sessionId") should be prefixed using the "$" in the path. The "sessionId" input parameter, in particular, needs not be included in the parameters dictionary, as it will be included automatically. The return package is always a Dictionary object with a root key of "value". Of course, it is your responsibility to research how to construct the command and input parameters (if any), and how to parse the return package.
'this test shows how to run a Selenium command (http end point) that is not currently wrapped in SeleniumVBA
'the command below (Firefox only) takes a screenshot of the entire page (beyond the viewport)
Dim driver As New WebDriver
Dim strB64 As String
'driver.DefaultIOFolder = ThisWorkbook.path '(this is the default)
driver.StartFirefox
driver.OpenBrowser
driver.NavigateTo "https://www.wikipedia.org/"
driver.Wait 1000
'all arguments in the command path string must be pefixed with "$", including sessionId
'command parameters (if required) can be passed as either a dictionary object or valid JSON string
strB64 = driver.ExecuteCmd("GET", "/session/$sessionId/moz/screenshot/full")("value")
'results in a base 64 encoded string which must be decoded into a bytearray before saving to file
driver.SaveBase64StringToFile strB64, ".\screenshotfull.png"
driver.Wait 1000
driver.CloseBrowser
driver.Shutdown
The ExecuteCDP Method. This method of the WebDriver
class provides a low-level interface to interact with the Chrome\Edge browsers using the Chrome DevTools Protocol. To use the ExecuteCDP
command, specify the command name (such as Page.captureScreenshot
) and if inputs arguments are required, a Dictionary
object containing input parameter key names and values. The return payload is always a Dictionary
with a root key of "value". The ExecuteCDP
packages the input arguments into a CDP JSON object, sends the command to the Webdriver through two special Selenium http endpoints (one for Edge and one for Chrome), and then receives the payload from the WebDriver. The process is not unlike what is performed for executing a normal Webdriver command. Beyond that, it is the User's responsibility to research how to construct the CDP command name and input parameters, and how to parse the return payload. Below is an example of executing one such command to produce an enhanced version of the SaveScreenshot
method, with more optionality.
'this demonstrates using the ExecuteCDP to perform a screenshot with enhanced user control
Dim driver As New WebDriver
Dim params As New Dictionary
'Dim clipRect As New Dictionary
Dim strB64 As String
driver.StartEdge
driver.OpenBrowser
driver.NavigateTo "https://www.wikipedia.org/"
driver.Wait 500
'see https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot
'construct the input parameters Dictionary
params.Add "format", "jpeg" 'jpeg, png (default), webp
params.Add "quality", 80 '0 to 100 (jpeg only)
'clip parameter can be used to snapshot an element rectangle
'(see GetRect method of the WebDriver and WebElement classes)
'clipRect.Add "x", 200
'clipRect.Add "y", 200
'clipRect.Add "width", 400
'clipRect.Add "height", 400
'clipRect.Add "scale", 1
'params.Add "clip", clipRect
'the next 3 paramters are currently marked as experimental (as of 11 June, 2023)
params.Add "captureBeyondViewport", True 'full screenshot
params.Add "fromSurface", True 'defaults to true
params.Add "optimizeForSpeed", False 'defaults to false
'qc the inputs in JSON format
Debug.Print WebJsonConverter.ConvertToJson(params, 4)
'send the cdp command to the WebDriver and return "data" key of the response Dictionary
strB64 = driver.ExecuteCDP("Page.captureScreenshot", params)("value")("data")
'results in a base 64 encoded string which must be decoded into a bytearray before saving to file
driver.SaveBase64StringToFile strB64, ".\screenshotfull.jpg"
driver.Wait 500
driver.CloseBrowser
driver.Shutdown
In SeleniumVBA, implicit and explicit waits are used to handle dynamic web pages where elements on the page may take time to load. The two types of waits allow you to pause the execution of your code until a certain condition is met.
Explicit waits are used to pause in-line execution for a specified time duration before continuing. For example, you can use explicit waits to wait for an element to be clickable, visible, or present on the page. Here's an example of an explicit wait in SeleniumVBA:
driver.NavigateTo "https://www.wikipedia.org/"
driver.Wait 1000 'pause in-line execution for 1 second
keySeq = "Leonardo da VinJci" & keys.Repeat(keys.LeftKey, 3) & keys.DeleteKey & keys.ReturnKey
driver.FindElement(By.ID, "searchInput").SendKeys keySeq
In the example above, the code pauses for 1 second (1000 ms) after navigating to the web page before finding and sending keystrokes to the searchInput element.
On the other hand, implicit waits are set at the driver level, and they affect all subsequent calls to find elements. For example, if you set an implicit wait of 10 seconds, every subsequent call to find an element will wait for up to 10 seconds for the element to be found. Implicit waits are useful for ensuring that your code doesn't fail due to slow-loading pages. Here is a similar example to the one above, but more time efficient, because the code only waits for the amount of time needed for FindElement
to find its target.
driver.ImplicitMaxWait = 10000 'wait up to 10 secs for subsequent calls to FindElement to find target element
driver.NavigateTo "https://www.wikipedia.org/"
keySeq = "Leonardo da VinJci" & keys.Repeat(keys.LeftKey, 3) & keys.DeleteKey & keys.ReturnKey
driver.FindElement(By.ID, "searchInput").SendKeys keySeq
In addition to the SetImplicitMaxWait
method, the SetScriptTimeout
and SetPageLoadTimeout
methods can be used to set how long to wait for a script to execute or a web page navigation respectively, before throwing a timeout error.
To execute JavaScript in SeleniumVBA, you need to call the ExecuteScript
method of the WebDriver
class. Here's an example:
driver.ExecuteScript("window.scrollBy(0, 1000)")
In the example above, the ExecuteScript
method is used to scroll down the page by 1000 pixels. The first argument to the ExecuteScript
method is a string that contains the JavaScript code to be executed. In this case, it's a simple script that scrolls the page.
It's also possible to pass arguments to the JavaScript code being executed. Here's an example:
result = driver.ExecuteScript("return arguments[0] * arguments[1];", 5, 10)
In this example, the ExecuteScript
method is passed two arguments, 5 and 10, and the JavaScript code in the first argument multiplies these two numbers and returns the result. The result of the script execution is stored in the result variable.
Note that any number of arguments can follow the script string argument. SeleniumVBA object arguments of type WebElement
, WebElements
, and WebShadowRoot
can also be passed to and returned from the ExecuteScript
method.
All SeleniumVBA methods that take file or folder path arguments support advanced path specifications for convenience and script portability.
System Environ Paths. SeleniumVBA recognizes the %[Environ]% syntax in all path specification strings. The example below sets the default storage location for downloading/updating Selenium WebDrivers:
Dim wdm As New WebDriverManager
'sets the default folder for storing the Selenium WebDrivers
wdm.DefaultDriverFolder = "%USERPROFILE%\Documents\SeleniumVBA\drivers"
'now check if Chrome driver exists and is up-to-date - if not then download a compatible driver version
Debug.Print wdm.AlignChromeDriverWithBrowser()
For reference, here are some useful path-related Environ values and corresponding path equivalencies:
%USERPROFILE%=[Drive]:\Users\[user name]
%APPDATA%=[Drive]:\Users\[user name]\AppData\Roaming
%LOCALAPPDATA%=[Drive]:\Users\[user name]\AppData\Local
%TEMP%=[Drive]:\Users\[user name]\AppData\Local\Temp
Relative Paths. All SeleniumVBA method path arguments support specification of a path relative to an assumed or prescribed base path. The base path can be set using the DefaultIOFolder
property of the WebDriver
class, or if not explicitly assigned, will default to the path of the active VBA project's host document location. Below are examples of various means to specify a relative path:
'driver.DefaultIOFolder = ThisWorkbook.Path 'this is the default base path location
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "https://it.wikipedia.org/wiki/Pagina_principale"
driver.Wait 1000
'save web page to html file
driver.PageToHTMLFile "source.html" 'saves to [ThisWorkbook.Path]\source.html
driver.PageToHTMLFile ".\source.html" 'saves to [ThisWorkbook.Path]\source.html
driver.PageToHTMLFile "..\source.html" 'saves to the parent folder of the folder where ThisWorkbook resides
driver.PageToHTMLFile ".\htmlfiles\source.html" 'saves to the htmlfiles folder located under the folder where ```ThisWorkbook``` resides
Please note that the path of the active VBA project's host document location [ThisWorkbook.Path
] is not necessarily the same as the path where the SeleniumVBA code library is located, which can be called externally.
OneDrive/SharePoint Paths. When using online cloud storage services such as OneDrive or SharePoint, SeleniumVBA will cast/convert any specified path to the associated local drive location, and not to the cloud location.
ResolvePath Method. This method of the WebDriver
class can be used to calculate and return an explicit absolute path, given a relative or symbolic path input. This may be useful for passing a full path to non-SeleniumVBA procedures and/or for testing. Here are several examples:
fullAbsolutePath = driver.ResolvePath(".\") 'returns [ThisWorkbook.Path]
fullAbsolutePath = driver.ResolvePath("%USERPROFILE%\Documents") 'returns the full path to the Documents folder
SeleniumVBA provides functionality for working with the three types of native popup messages offered by JavaScript: "alert", "confirm", and "prompt" alerts:
- The "alert" type consists of a message to the user, and a single "OK" button which the user clicks to acknowledge/dismiss the alert.
- The "confirm" alert is similar to an "alert", except that the user is presented with two buttons: "accept" and "cancel".
- The "prompt" alert is similar to a "confirm", except that it also includes a text box for accepting user input. Similar to working with form elements, you can send keys to fill in a response. Pressing the cancel button will not submit any text.
Automated interaction with alerts is facilitated by the WebAlert
class which has methods for accepting the alert, dismissing the alert, sending keys, and retrieving the alert message. The WebAlert
object is instantiated via the SwitchToAlert
method of the WebDriver
class. The SwitchToAlert
method waits a user-specified maximum amount of time via the maxWaitTimeMS
argument (default 10 secs) for an expected alert to show, and then returns a WebAlert
object for subsequent interaction. The example below shows this process:
Dim driver As New WebDriver
Dim keys As New WebKeyboard
driver.StartFirefox
driver.OpenBrowser
driver.ImplicitMaxWait = 2000
driver.NavigateTo "https://the-internet.herokuapp.com/javascript_alerts"
'find and then click on an element that throws a prompt-type alert
driver.FindElement(By.XPath, "//*[@id='content']/div/ul/li[3]/button").Click
'SwitchToAlert waits up to a user-specified max time (default = 10 secs)
'for alert to show, and then returns a WebAlert object for interaction
driver.SwitchToAlert.SendKeys("hola mi nombre es Jose").Accept
Debug.Print driver.FindElementByID("result").GetText
driver.CloseBrowser
driver.Shutdown
Be aware that the only WebDriver commands that should be executed between the show Alert event (e.g. after Click
in the example above) and SwitchToAlert.Accept/Dismiss
are Wait
, SwitchToAlert.GetText
, and SwitchToAlert.SendKeys
- other commands executed in the time interval while waiting for user response could interfere with alert interaction.
Also, as of July 2023, Chrome and Edge have a ("WontFix") reported bug where sending text to a prompt alert via SwitchToAlert.SendKeys
does not display in the text input field but otherwise DOES actually receive the key input. The bug has been classified as a "display-only issue". Firefox does not have this problem.
The WebKeyboard Class. This class is a helper utility for automating keyboard-browser interactions. Use in conjunction with the WebDriver
object's SendKeys
and SendKeysToOS
methods, and with the WebActionChain
object's key-related methods. The class consists of methods for representing special keys such as “Enter”, “Shift”, and many more. It also contains several functions for easily generating a concatenated string of multiple key presses, which will be covered more below in the context of how to use them.
WebDriver/WebElement's SendKeys Method. The SendKeys
method sends a key sequence to the specified element. The key sequence is represented by a concatenated string of individual keyboard keys, which can include special keys on the keyboard.
The SendKeys
method automatically sets the focus on the target element. It includes an optional argument, clearBeforeTyping
for first clearing the input field in the case where it already contains text (defaults to False).
The modifier keys (Shift, Ctrl, Alt) when located in a key sequence are considered "sticky", i.e., they are interpreted as consisting of a key press down, without immediately being followed by a key press up. Keys that follow sticky modifier(s) are modified if/until the special Null key is encountered (see the NullKey
method of the WebKeyboard
class), which triggers release of all previous modifiers used in the string.
For a more structured approach to using modifier keys, consider the Chord
method of the WebKeyboard
class. This method allows one to specify a sequence of modifier key(s) applied to a sequence of non-modifier keys, with explicit release of the modifier(s) as part of the construct.
Dim keys As New WebKeyboard
...
'send a Ctrl-a and delete key to clear the input field and then some text to the element
element.SendKeys keys.Chord(keys.ControlKey, "a") & keys.DeleteKey & "my text"
Additionally, the Repeat
method of the WebKeyboard
class is useful for concatenating a specified sub-string repeated a specified number of times.
Dim keys As New WebKeyboard
...
'send three tab keys and an enter key to the element
element.SendKeys keys.Repeat(keys.TabKey, 3) & keys.EnterKey
WebActionChain's SendKeys Method. This method functions similarly to the SendKeys
method of the WebDriver/WebElement
classes. See Working with Action Chains for more information.
WebDriver SendKeysToOS Method. SendKeysToOS
can be useful for sending keyboard input to non-browser OS windows, as well as when the standard SendKeys
method does not function with the browser window of interest. If the target window title is known, then specify the (can be partial) title text via the windowTitle
argument. For the case where the window title is not known, then the SendKeysToOS
will send to the window that is active at the time the keyboard input is sent.
For inline execution-blocking windows such as "Select Certificate" popup, SendKeysToOS
can be launched to run on its own separate thread, with a delay, prior to the command that triggers the target window. This is achieved by setting the runOnSeparateThread
argument to True.
If running on a separate thread, there are two ways of delaying the SendKeys
event until the target window is activated. If the window title is known and has been specified, then set waitForWindow
to True, and specify the maxTimeToWaitMS
to delay input until the window shows. Note that the arguments waitForWindow
and maxTimeToWaitMS
are relevant only in the case where the window title has been specified.
If running on a separate thread and the window title is not known, then specify an explicit amount of time via the timeDelayMS
. Some experimentation may be necessary to get the timing correct for sending key input only after the non-titled window has been activated.
The following code example shows how to handle an execution-blocking input box that has a known title. For an example of an execution-blocking window of unknown title, see the Select Certificate discussion.
Dim driver As New WebDriver
Dim keys As New WebKeyboard
driver.StartChrome
driver.OpenBrowser
'Sends input to the future InputBox via separate thread
driver.SendKeysToOS _
keyString:="Here is my input to InputBox!" & keys.EnterKey, _
windowTitle:="InputBox Title", _
runOnSeparateThread:=True, _
waitForWindow:=True, _
maxTimeToWaitMS:=2000, _
timeDelayMS:=0
driver.NavigateTo "https://example.com/"
driver.Wait 1000
'this InputBox dialog will block execution flow until it receives input
'so must send input keys from separate thread launched earlier
ThisWorkbook.Activate
'InputBox returns the user input keys
Debug.Print InputBox("Please enter input keys:", "InputBox Title")
driver.Wait 1000
driver.CloseBrowser
driver.Shutdown
SeleniumVBA exposes the WebWindow
and WebWindows
classes for managing multiple browser windows and/or tabs, without the need for using window handles.
A WebWindow
object is useful for determining the window’s activation status, activating the window, setting position and size of the window, retrieving the window’s title and handle, and closing the window. To instantiate a WebWindow
object, use the ActiveWindow
method of the WebDriver
class to return the active WebWindow
object, or use one of the several methods available in the WebWindows
class (see below).
The Windows
method of the WebDriver
class returns the WebWindows
object, which is a collection of open browser WebWindow
objects. The WebWindows
object is useful for enumerating the collection of WebWindow
objects, opening a new window, switching to a window by its title or ordinal position in the collection, and returning the active WebWindow
object.
Aside from the non-object-oriented inconvenience of using handles to reference windows, the Selenium WebDriver has several other short-comings with respect to windows management that SeleniumVBA seeks to address:
- If a window is spawned by clicking an HTML link or executing JavaScript, the Selenium WebDriver has no way of knowing which window the operating system considers active.
- The window handles collection is not guaranteed to be in the order in which the associated windows were opened.
- When a window is closed, there is no window that is considered active without explicitly activating one.
Switching Windows or Tabs. To get around the first two issues listed above, Selenium recommends this handle-based work-around demonstrated in the following:
Dim driver As New WebDriver
Dim mainHandle As String
Dim allHandles As Collection
Dim childHandle As String
Dim i As Long
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "http://the-internet.herokuapp.com/windows"
'get the handle to the current active window
mainHandle = driver.ActiveWindow.Handle
'spawn new window
driver.FindElementByCssSelector("#content > div > a").Click
'note here that main window is still the active one from Selenium's perspective!!
Debug.Print driver.ActiveWindow.Title 'prints "The Internet"
'now get the collection of all open window handles
Set allHandles = driver.Windows.Handles
'find the child window's handle by elimination
For i = 1 To allHandles.Count
If allHandles(i) <> mainHandle Then
childHandle = allHandles(i)
Exit For
End If
Next i
'activate the child window and print title
driver.Windows(childHandle).Activate
Debug.Print driver.ActiveWindow.Title 'prints "New Window"
driver.ShutDown
However, SeleniumVBA offers the following more object-oriented and direct approaches to accomplish the same. Below shows an object-oriented approach by enumerating the WebWindows
object:
Dim driver As New WebDriver
Dim mainWindow As WebWindow
Dim window As WebWindow
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "http://the-internet.herokuapp.com/windows"
'get the current active window
Set mainWindow = driver.ActiveWindow
'spawn a new window
driver.FindElementByCssSelector("#content > div > a").Click
'note here that main window is still the active one from Selenium's perspective!!
Debug.Print driver.ActiveWindow.Title 'prints "The Internet"
'find and activate the child window and then print its title
For Each window In driver.Windows
If window.IsNotSameAs(mainWindow) Then
Debug.Print window.Activate.Title 'prints "New Window"
Exit For
End If
Next window
driver.Shutdown
If the open windows can be differentiated by using their titles or urls, then the following more direct approach using either the SwitchToByTitle
or the SwitchToByUrl
method can be employed:
Dim driver As New WebDriver
Dim mainWindow As WebWindow
Dim childWindow as WebWindow
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "http://the-internet.herokuapp.com/windows"
'get the current active window
Set mainWindow = driver.ActiveWindow
'spawn a new window
driver.FindElementByCssSelector("#content > div > a").Click
'note here that main window is still the active one from Selenium's perspective!!
Debug.Print driver.ActiveWindow.Title 'prints "The Internet"
'Set childWindow = driver.Windows.SwitchToByUrl("https://the-internet.herokuapp.com/windows/new")
Set childWindow = driver.Windows.SwitchToByTitle("New Window")
Debug.Print driver.ActiveWindow.Title 'prints "New Window"
Debug.Print childWindow.Title 'prints "New Window"
driver.Shutdown
And lastly, if window titles/urls are not unique, then the SwitchToNext
method will return the next window AFTER the active window. If the active window is the last window in the collection, then the method will "wrap" to activate the first window in the collection:
Dim driver As New WebDriver
Dim mainWindow As WebWindow
Dim childWindow as WebWindow
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "http://the-internet.herokuapp.com/windows"
'get the current active window
Set mainWindow = driver.ActiveWindow
'spawn a new window
driver.FindElementByCssSelector("#content > div > a").Click
'note here that main window is still the active one from Selenium's perspective!!
Debug.Print driver.ActiveWindow.Title 'prints "The Internet"
'switch to the next open window in the collection AFTER the current active window
Set childWindow = driver.Windows.SwitchToNext
Debug.Print driver.ActiveWindow.Title 'prints "New Window"
Debug.Print childWindow.Title 'prints "New Window"
driver.Shutdown
Opening a New Tab or Window. Opening a new window with SeleniumVBA can be performed using the SwitchToNew
method of the WebWindows
class, while specifying the type of window ("window" or "tab") to open.
Dim driver As New WebDriver
Dim win1 As WebWindow
Dim win2 As WebWindow
Dim i As Long
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "http://the-internet.herokuapp.com/windows"
'get the current active window
Set win1 = driver.ActiveWindow
'open and activate a new Window-type window
Set win2 = driver.Windows.SwitchToNew(windowType:=svbaWindow)
'Set win2 = driver.Windows.SwitchToNew(windowType:=svbaTab) 'for Tab-type window
driver.NavigateTo "http://google.com"
For i = 1 To 5
Debug.Print win1.Activate.Title
Debug.Print win2.Activate.Title
Next i
driver.Shutdown
Closing a Tab or Window. As mentioned above, the Selenium documentation states that after closing a window, the user must explicitly switch to another window, even if there is only one window remaining. However, in SeleniumVBA's implementation, closing a window through the SeleniumVBA object model will automatically activate the previously active window, or the only remaining open window if only one exists.
Dim driver As New WebDriver
Dim mainWindow As WebWindow
Dim childWindow as WebWindow
driver.StartChrome
driver.OpenBrowser
driver.NavigateTo "http://the-internet.herokuapp.com/windows"
'get the current active window
Set mainWindow = driver.ActiveWindow
'spawn a new window
driver.FindElementByCssSelector("#content > div > a").Click
'note here that main window is still the active one from Selenium's perspective!!
Debug.Print driver.ActiveWindow.Title 'prints "The Internet"
Set childWindow = driver.Windows.SwitchToNext
Debug.Print driver.ActiveWindow.Title 'prints "New Window"
childWindow.CloseIt 'this automatically activates the mainWindow upon close
Debug.Print driver.ActiveWindow.Title 'prints "The Internet"
Debug.Print mainWindow.Title 'prints "The Internet"
driver.Shutdown
Modifying a Window's Bounds or State. The WebWindow
class has methods/properties to modify the size, position, and visibility state of an open window. The following example maximizes a window, changes its position, and reduces its height by 50%.
Dim driver As New WebDriver
Dim winBounds As Dictionary
driver.StartEdge
driver.OpenBrowser
driver.NavigateTo "https://www.wikipedia.org/"
'get the current bounds dictionary object
Set winBounds = driver.ActiveWindow.Bounds
Debug.Print "current window position/size", winBounds("x"), winBounds("y"), winBounds("width"), winBounds("height")
'maximize the window state
driver.ActiveWindow.Maximize
'get the maximized bounds dictionary object
Set winBounds = driver.ActiveWindow.Bounds
Debug.Print "maximized window position/size", winBounds("x"), winBounds("y"), winBounds("width"), winBounds("height")
'modify the position and size
winBounds("y") = 200
winBounds("height") = winBounds("height") / 2
'set the new window bounds
Set driver.ActiveWindow.Bounds = winBounds
'these short-cut methods can be used to do above as well
'driver.ActiveWindow.SetSize height:=winBounds("height") / 2
'driver.ActiveWindow.SetPosition y:=200
'get the modified bounds dictionary object
Set winBounds = driver.ActiveWindow.Bounds
Debug.Print "modified window position/size", winBounds("x"), winBounds("y"), winBounds("width"), winBounds("height")
driver.Shutdown
Full Disclosure: Some of the above text was generated by ChatGPT and then modified to suit the needs of this Wiki page.