How to click on "Launch Application" dialog in Firefox with Selenium? - selenium

I have a Selenium test, in which I need to click on a "cliclient://" link, and that link needs to open an application. Now, I need to create a new profile for each test, and I don't know how to bypass the "Launch Application" dialog that appears when clicking on the link:
Here's a snippet of the test that I've created:
profile = Selenium::WebDriver::Firefox::Profile.new
profile.secure_ssl = false
profile.assume_untrusted_certificate_issuer=true
profile["plugin.default.state"] = 2
profile["plugin.state.java"] = 2
profile["browser.download.folderList"] = 2
profile["browser.download.manager.alertOnEXEOpen"] = false
profile["browser.download.manager.closeWhenDone"] = true
profile["browser.download.manager.focusWhenStarting"] = false
profile["browser.download.manager.showWhenStarting"] = false
profile["browser.helperApps.alwaysAsk.force"] = false
profile["browser.helperApps.neverAsk.saveToDisk"] = 'application/x-msdownload,application/octet-stream, application/x-msdownload, application/exe, application/x-exe, application/dos-exe, vms/exe, application/x-winexe, application/msdos-windows, application/x-msdos-program'
profile["gfx.direct2d.disabled"] = true
profile["layers.acceleration.disabled"] = true
What is it in the profile that I need to set, to bypass the dialog, or to somehow click on OK when this dialog appears?

You can also try using SikuliX http://sikulix.com/ which is an automation software which uses images to recognise the GUI elements on which certain actions need to be performed
Hovever to use it with ruby you will most probably need to compile and run a java class via a system command and also you will need JDK installed on the machine where the automation will be performed

Use C# to access the Win32 API and find the handle of the window with the title "Launch Application'. You'll need to use this, as the window is controlled by the OS and therefore Selenium can't interact with it. Then use the same API to click the cancel button (find its identifying properties using WinSpy)
Sorry if this isn't a full answer but I couldn't merely comment as I have insufficient rep at the moment.

The idea is creating a profile with default schemes you want and let selenium initialize by copying this profile. The key file that contains default schemes is handlers.json rather than prefs.js
Below is the proof of concept in python.
import json
import os
from pathlib import Path
from selenium import webdriver
# %APPDATA%\Mozilla\Firefox\Profiles\oa6m3bc6.default\Preferences\handlers.json
SCHEME_NAME = 'xxxxx'
PROFILE_DIR_NAME = 'xxxxxx'
handlers = {
'defaultHandlersVersion': {'en-US': 4},
'schemes': {SCHEME_NAME: {'action': 4}}
}
profile_dir = Path(os.environ['APPDATA']) / f'Mozilla/Firefox/Profiles/{PROFILE_DIR_NAME}'
profile_dir.mkdir(exist_ok=True)
with open(profile_dir / 'handlers.json', 'w+') as f:
json.dump(handlers, f)
profile = webdriver.FirefoxProfile(profile_dir)
driver = webdriver.Firefox(profile)
driver.get('http://example.com/')
Tested with Firefox 71.0, geckodriver 0.26.0 (e9783a644016 2019-10-10 13:38 +0000), Python 3.7, and Windows 10

Related

Send a numpad key (with Num Lock off)

I want to test how my app reacts to numpad keys. I found in https://www.w3.org/TR/webdriver/ specs that for example for Numpad Home (with location = DOM_KEY_LOCATION_NUMPAD = 3) a symbol \uE057 should be used. However, it doesn't work for me: I get Home with default location (0), moreover, event.code is empty. It gives me a different result when I physically press NUMPAD7 button with Num Lock off: it that case, I get correct location 3 and event.code is Numpad7.
var options = FirefoxOptions();
options.setLogLevel(FirefoxDriverLogLevel.TRACE);
var driver = FirefoxDriver(options);
driver.navigate().to("https://keycode.info/");
driver.findElementByTagName("body").sendKeys("\uE057");
So how can I send such a key? I'm now thinking of manual recording of generated events when I physically press a key, and then sending these events via Selenium's execution of JS script. However, I haven't tried it yet; maybe there is a better way to do it in Selenium; maybe there is another framework that allows it better.
By the way, I've filed a similar ticket in geckodriver because it looks like a bug of webdriver to me...
\ue01d is the unicode for NUmberpad3
python code:
from selenium import webdriver
from selenium import webdriver
options = webdriver.ChromeOptions()
driver = webdriver.Chrome()
driver.get("https://keycode.info/")
driver.find_element_by_tag_name("body").send_keys("\ue01d")
input()
you can use https://www.javadoc.io/doc/org.seleniumhq.selenium/selenium-api/latest/org/openqa/selenium/Keys.html to send keys instead of sending unicode directly
In firefox this is will work if you use action chain:
ActionChains.send_keys("\ue01d").perform()

How to autoupdate chromeDriver & geckDriver in selenium

I have selenium grid setup with multiple node machines , where in I am manually downloading chromeDriver & geckoDriver on all selenium node machines & using them for chrome & firefox browsers respectively.
Now here chrome & firefox browsers (on all selenium node machines) are set on 'Automatic Update' (which is required as I want my application to be tested always on latest browser versions) , because of this browsers on my node machines keep getting updated more often & since respective driver updates is a manual process , it forces me to log in to each selenium node machine & update them manually.
Can this process be automated ?
PS : I know that dockerized selenium grid can be used to fetch/pull latest browser images & their drivers , however switching from traditional selenium grid to dockerized selenium grid is another thing & will take some time to implement.
I would argue that your current approach is not a feasible approach. New versions of browsers are released with zero consideration for Selenium (or any other drivers). As soon as a new browser update is released, there is a reasonably high chance that there will be NO existing driver that works with that version. It often takes days for Selenium teams to release updated drivers to match the newest version of a browser.
And since you are automatically updating your browsers, then you are possibly automatically breaking your Selenium tests until a new driver version is released, or until you downgrade the browsers.
Now, you may be fine with that, and are okay with disabling a browser's tests until the most current Selenium drivers work with the most current browser version. If that is the case, then here are some solutions:
1) If you are using C#, store your drivers in the test solution as a Nuget package, or in a dependencies folder. Then, have the automation reference that driver no matter where it is running. When you need to update the driver, you literally only need to update it in one place, and check in the changes. All client machines will, through your CI process, pull down the latest code, which includes that new driver.
2) If for some reason you do not want the driver in your project as a Nuget package or a manually-saved dependency, then have your CI handle the update process. Point your automation code to a driver located in some common directory on whatever client machine it is currently running on -> wherever your machine stores the dependencies after downloading them. For example; downloading selenium files via console on a Windows machine will put them somewhere in %APPDATA% "C:\Users\xxxxxx\AppData\Roaming\npm\node_modules". That is where your test solution should look.
Then, in your CI scripts, before running any tests, download the latest driver. The syntax is nearly the same, if not identical between Windows and Linux/Unix Kernels. This assumes you have npm installed.
npm install -g selenium
If you already have latest, then nothing will happen. If you don't the latest driver will be downloaded by your CI script before running tests. Then, your test solution will be pointing to where the driver is stored on the client, and it will automatically be using the newest driver.
First #Asyranok is right, even when implemented auto update code will not work 100% of the time. However, for many of us, this occasional downtime is "ok" as long as it's just a few days.
I've found that manually updating X servers every few months to be incredibly irritating and while there are well written instructions on the selenium website on how to "auto update" the driver I've yet to see one openly available non-library implementation of this guide.
My answer is specific to C#, for this language the solution typically suggested is to use NuGet to pull the latest driver automatically, this has two issues:
You need to deploy at the frequency of chrome updating (most companies aren't there yet, neither are we) or your application will be "broken" for the time between chrome updating and your "new" version of the application deploying, and again this is only if you release on a schedule, if you release ad-hoc your going to have to go through a series of manual steps to update, build, release etc. to get your application working again.
You need (typically, without a work around) to pull the latest chromedrive from NuGet by hand, again a manual process.
What would be nice would be what python has and #leminhnguyenHUST suggests which is using a library that will automatically pull the latest chromedriver on runtime. I've looked around and haven't yet found anything for C# that does this, so I decided to roll my own and build that into my application:
public void DownloadLatestVersionOfChromeDriver()
{
string path = DownloadLatestVersionOfChromeDriverGetVersionPath();
var version = DownloadLatestVersionOfChromeDriverGetChromeVersion(path);
var urlToDownload = DownloadLatestVersionOfChromeDriverGetURLToDownload(version);
DownloadLatestVersionOfChromeDriverKillAllChromeDriverProcesses();
DownloadLatestVersionOfChromeDriverDownloadNewVersionOfChrome(urlToDownload);
}
public string DownloadLatestVersionOfChromeDriverGetVersionPath()
{
//Path originates from here: https://chromedriver.chromium.org/downloads/version-selection
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe"))
{
if (key != null)
{
Object o = key.GetValue("");
if (!String.IsNullOrEmpty(o.ToString()))
{
return o.ToString();
}
else
{
throw new ArgumentException("Unable to get version because chrome registry value was null");
}
}
else
{
throw new ArgumentException("Unable to get version because chrome registry path was null");
}
}
}
public string DownloadLatestVersionOfChromeDriverGetChromeVersion(string productVersionPath)
{
if (String.IsNullOrEmpty(productVersionPath))
{
throw new ArgumentException("Unable to get version because path is empty");
}
if (!File.Exists(productVersionPath))
{
throw new FileNotFoundException("Unable to get version because path specifies a file that does not exists");
}
var versionInfo = FileVersionInfo.GetVersionInfo(productVersionPath);
if (versionInfo != null && !String.IsNullOrEmpty(versionInfo.FileVersion))
{
return versionInfo.FileVersion;
}
else
{
throw new ArgumentException("Unable to get version from path because the version is either null or empty: " + productVersionPath);
}
}
public string DownloadLatestVersionOfChromeDriverGetURLToDownload(string version)
{
if (String.IsNullOrEmpty(version))
{
throw new ArgumentException("Unable to get url because version is empty");
}
//URL's originates from here: https://chromedriver.chromium.org/downloads/version-selection
string html = string.Empty;
string urlToPathLocation = #"https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + String.Join(".", version.Split('.').Take(3));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToPathLocation);
request.AutomaticDecompression = DecompressionMethods.GZip;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
html = reader.ReadToEnd();
}
if (String.IsNullOrEmpty(html))
{
throw new WebException("Unable to get version path from website");
}
return "https://chromedriver.storage.googleapis.com/" + html + "/chromedriver_win32.zip";
}
public void DownloadLatestVersionOfChromeDriverKillAllChromeDriverProcesses()
{
//It's important to kill all processes before attempting to replace the chrome driver, because if you do not you may still have file locks left over
var processes = Process.GetProcessesByName("chromedriver");
foreach (var process in processes)
{
try
{
process.Kill();
}
catch
{
//We do our best here but if another user account is running the chrome driver we may not be able to kill it unless we run from a elevated user account + various other reasons we don't care about
}
}
}
public void DownloadLatestVersionOfChromeDriverDownloadNewVersionOfChrome(string urlToDownload)
{
if (String.IsNullOrEmpty(urlToDownload))
{
throw new ArgumentException("Unable to get url because urlToDownload is empty");
}
//Downloaded files always come as a zip, we need to do a bit of switching around to get everything in the right place
using (var client = new WebClient())
{
if (File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip"))
{
File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip");
}
client.DownloadFile(urlToDownload, "chromedriver.zip");
if (File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip") && File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.exe"))
{
File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.exe");
}
if (File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip"))
{
System.IO.Compression.ZipFile.ExtractToDirectory(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip", System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
}
}
}
Then usually I'll stick this very hacky invocation at the beginning of my application to invoke this feature and ensure that the latest chromedriver is available for my application:
//This is a very poor way of determining if I "need" to update the chromedriver,
//however I've yet to figure out a better way of doing this...
try
{
using (var chromeDriver = SetupChromeDriver())
{
chromeDriver.Navigate().GoToUrl("www.google.com");
chromeDriver.Quit();
}
}
catch
{
DownloadLatestVersionOfChromeDriver();
}
I'm sure this could be improved significantly, but so far it's worked for me.
Note: Cross Posted Here
I know its a bit of an old question, but I thought this could also be helpful to people: https://github.com/rosolko/WebDriverManager.Net. Its a Nuget package (https://www.nuget.org/packages/WebDriverManager/) that appears to solve the problem using .NET.
One option for Java based solution is the Bonigarcia Webdrivermanager. Seems like it has inbuilt support for remote web driver, using this drivermanager is able to download the latest browsers on the Hub of the selenium grid . Check this post.
One possible solution - write a script that downloads the latest Webdriver version and schedule its daily launch.
For example, here is Python 3 script that downloads the latest Chromedriver for Linux of version that matches currently installed Chrome browser version
import os
import re
import requests
import zipfile
XML_INFO_URL = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE_'
DRIVER_PATH = 'https://chromedriver.storage.googleapis.com'
ZIPPED_DRIVER_FILE_NAME = 'chromedriver_linux64.zip'
UNZIPPED_DRIVER_FILE_NAME = 'chromedriver'
PATH_TO_CHROMEDRIVER = '.'
def get_browser_major_version():
stream = os.popen('google-chrome --version')
output = stream.read()
version_info_str = re.search(r'\d+\.\d+\.\d+', output).group(0)
return re.search(r'^\d+', version_info_str).group(0)
def get_driver_latest_version(browser_major_version):
return requests.get(XML_INFO_URL + browser_major_version).text
def download_file(url, file_name):
file = requests.get(url)
with open(file_name, "wb") as code:
code.write(file.content)
driver_file_url = DRIVER_PATH + '/' + get_driver_latest_version(get_browser_major_version()) + '/' + ZIPPED_DRIVER_FILE_NAME
download_file(driver_file_url, ZIPPED_DRIVER_FILE_NAME)
with zipfile.ZipFile(ZIPPED_DRIVER_FILE_NAME, 'r') as zip_ref:
zip_ref.extractall(PATH_TO_CHROMEDRIVER)
os.chmod(PATH_TO_CHROMEDRIVER + '/' + UNZIPPED_DRIVER_FILE_NAME, 0o744)
os.remove(ZIPPED_DRIVER_FILE_NAME)
Now the problem has been solved with webdrivermanager module from here
Description:
Python module to facilitate downloading and deploying WebDriver binaries. The classes in this module can be used to automatically search for and download the latest version (or a specific version) of a WebDriver binary and then extract it and place it by copying or symlinking it to the location where Selenium or other tools should be able to find it then.
Hope it helps !!!
I had the same situation. I came up with the below solution to automatically update the driver by checking chrome compatibility,
update_chromedriver.py
import zipfile
from bs4 import BeautifulSoup
import requests
import re
from selenium import webdriver
from selenium.common.exceptions import SessionNotCreatedException
from selenium.webdriver.chrome.options import Options
downloads_url = "https://chromedriver.chromium.org/downloads"
basic_downloads_path = 'https://chromedriver.storage.googleapis.com/{0}chromedriver_{1}.zip'
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
# os='win32'
os = 'linux64'
driver_executable = './driver/chromedriver'
def check_browser():
driver = None
try:
driver = webdriver.Chrome(executable_path=driver_executable, options=options)
driver.get('https://www.google.com/')
return True
except SessionNotCreatedException:
return False
finally:
if driver is not None:
driver.close()
def download_zip(_url):
r = requests.get(_url, allow_redirects=True)
open('./driver/chrome.zip', 'wb').write(r.content)
with zipfile.ZipFile('./driver/chrome.zip') as z:
z.extractall('./driver/')
def get_versions():
html = requests.get(downloads_url).text
soup = BeautifulSoup(html, "html.parser")
versions_a = soup.find_all('a', href=re.compile('https://chromedriver.storage.googleapis.com/index.html\\?path='))
versions = []
for a in versions_a:
version = a['href'].split('=', 1)[1]
versions.append(version)
return versions
def auto_update_driver():
print('====> Checking current Chrome compatibility.')
compatible = check_browser()
print('====> It is ' + ('Compatible.' if compatible else 'Not Compatible.'))
if not compatible:
print('====> Trying to find alternate versions.')
versions = get_versions()
print(f'==> ({len(versions)}) found.')
for version in versions:
url = basic_downloads_path.format(version, os)
download_zip(url)
compatible = check_browser()
print(f'==> Trying version {version} - {"Compatible" if compatible else "Not Compatible"}')
if compatible:
break
if not compatible:
raise Exception('Unable to upgrade chrome version. Probably you are using very old chrome version.'
'Please update chrome and try again!!')
Please note :
My chrome driver is in the current path ./driver/
This is in my ubuntu machine. Please change accordingly to your requirements
Usage of the above python script
from update_chromedriver import auto_update_driver
auto_update_driver()
you can use ansible or puppet to take update package on all of the node

Selenium action 'move_to_element' doesn't work in Safari because of usupported 'pause' command

The next command is failed on Safari browser during automation testing:
ActionChains(driver).move_to_element(searchInput).perform()
Exception:
InvalidArgumentException: Message: Encountered key input source with
invalid 'value' in payload: {actions = ({duration = 0;type =
pause;});
id = key;
type = key;}
The whole refined test example:
def test_safari2(self):
driver = webdriver.Safari()
driver.get('https://www.wikipedia.org')
locator = (By.ID, 'searchInput')
# 1. the line below is passed
searchInput = WebDriverWait(driver, timeout=30).until(expected_conditions.visibility_of_element_located(locator))
# 2. the line below is failed in Safari, but passed in Chrome, FF
ActionChains(driver).move_to_element(searchInput).perform()
However! If self.w3c_actions.key_action.pause() is commented inside action move_to_element(), then the whole Action chains works!
def move_to_element(self, to_element):
"""
Moving the mouse to the middle of an element.
:Args:
- to_element: The WebElement to move to.
"""
if self._driver.w3c:
self.w3c_actions.pointer_action.move_to(to_element)
# self.w3c_actions.key_action.pause()
else:
self._actions.append(lambda: self._driver.execute(
Command.MOVE_TO, {'element': to_element.id}))
return self
The similar situation with other actions.
My question is:
Is it is known limitation of Safari? And therefore ActionChais command could not be use for Selenium + Safari? Or there is some configuration pecularity?
My test runner configuration:
OS: Mac HighSierra 10.13.6
Safari 12.0 (13606.2.11)
Selenium: 3.14.1
Python: 2.7.14
Safari is started with w3c capabilities and protocol (i.e. driver.w3c=True)
Issue background:
I have an enough developed framework with a lot of actions and tests that work Ok for Chrome and Firefox. Now I'm trying to extend coverage for Safari browser also. So, that is why I'm searching for solution for not working ActionChains
Workaround by wrapping ActionChains class so that key_action.pause is not used (which does not seem to serve any important purpose):
import selenium.webdriver
class ActionChains(selenium.webdriver.ActionChains):
def __init__(self, driver):
super(ActionChains, self).__init__(driver)
if driver.name in ('Safari', 'Safari Technology Preview'):
self.w3c_actions.key_action.pause = lambda *a, **k: None

Threading and Selenium

I'm trying to make multiple tabs in Selenium and open a page on each tab simultaneously. Here is the code.
CHROME_DRIVER_PATH = "C:/chromedriver.exe"
from selenium import webdriver
import threading
driver = webdriver.Chrome(CHROME_DRIVER_PATH)
links = ["https://www.google.com/",
"https://stackoverflow.com/",
"https://www.reddit.com/",
"https://edition.cnn.com/"]
def open_page(url, tab_index):
driver.switch_to_window(handles[tab_index])
driver.get(url)
return
# open a blank tab for every link in the list
for link in range(len(links)-1 ): # 1 less because first tab is already opened
driver.execute_script("window.open();")
handles = driver.window_handles # get handles
all_threads = []
for i in range(0, len(links)):
current_thread = threading.Thread(target=open_page, args=(links[i], i,))
all_threads.append(current_thread)
current_thread.start()
for thr in all_threads:
thr.join()
Execution goes without errors, and from what I understand this should logically work correctly. But, the effect of the program is not as I imagined. It only opens one page at a time, sometimes it doesn't even switch the tab... Is there a problem that I'm not aware of in my code or threading doesn't work with Selenium?
There is no need in switching to new window to get URL, you can try below to open each URL in new tab one by one:
links = ["https://www.google.com/",
"https://stackoverflow.com/",
"https://www.reddit.com/",
"https://edition.cnn.com/"]
# Open all URLs in new tabs
for link in links:
driver.execute_script("window.open('{}');".format(link))
# Closing main (empty) tab
driver.close()
Now you can handle (if you want) all the windows from driver.window_handles as usual

Run debug configuration programmatically

I use Chrome DevTools to debug java script code and I need it to run programmatically from my plugin.
(If I understood the slightly brief question.)
You want to make a ILaunchConfigurationWorkingCopy, set the attributes on it, optionally save it, then launch it.
The Launch Manager is very useful as you can do stuff with launches using it.
Here is a simple example:
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType launchType = launchMgr.getLaunchConfigurationType("type id (from plugin.xml)");
ILaunchConfigurationWorkingCopy wc = launchType.newInstance(null, manager.generateLaunchConfigurationName("Name Here"));
wc.setAttributes(launchAttributes);
ILaunchConfiguration lc = wc.doSave();
Launch launch = lc.launch(ILaunchManager.DEBUG_MODE, new NullProgressMonitor());