Xvfb, Jenkins, Selenium tests - Capture Screenshots of all pages - selenium

I'm trying to find some clues on the following issues and not able to find good help online.
I'm running Xvfb (X virtual frame buffer), firefox on a Linux machine in headless mode. Xvfb main service is up and running and DISPLAY variable is set.
/usr/bin/Xvfb :99 -ac -screen 0 1600x1200x16
I have some automated selenium based tests which I'm running using Gradle (gradle test). They run successfully and in Jenkins I'm able to get this working using Xvfb plugin. JUnit post publish report/result info and Gradle's reports/test/index.html file is showing successful test run.
I just run the following to run tests in Gradle:
gradle test -DsomePropConfigFileForEnv=SomeSourceConfigFilewithPathvalue
My questions:
1. How can I get the screenshots of all the pages that this automated test/run is rendering (i.e. login page, application main page after login, user clicks on the main page here and there (i.e. opening/clicking on various tabs, links, tables, buttons etc) and finally log out page.
I'm able to get the screenshot from the Xvfb_screen<N> file, which is getting created under -fbdir folder (what we specify while running Xvfb via a Jenkins job) but the screenshot is a Black page if test runs successfully (this can be due to the 2nd bullet I mentioned below) --OR it's a valid single page image screenshot (if an error is encountered during the test run).
I'm trying to get all the pages which the automated Selenium tests are rendering (the config file I passed to Gradle as a -D parameter has URLs / user name / browser, version etc info in it). PS: It's not just for some random URL that I'm trying to get an image screenshot using Xvfb DISPLAY virtual frame buffer.
During the test, I see there's a valid virtual framebuffer file, with a valid size.
For ex: While Jenkins job is in progress and running Gradle test task and Xvfb plugin has started a new xvfb instance, I see:
/production/JSlaves/kobaloki2_1/xvfb-2015-02-04_01-16-37-6170319257811815857.fbdir/Xvfb_screen0
but as soon as the test is complete (or errors our), this file is getting deleted from this xxxx.fbdir folder and there's no file at all.
Why is this file getting deleted.
If it'll remain there, then I can use xwd/xwud command and other tools (imagemagick convert etc commands) to create an image file as a POST BUILD action or even within the BUILD section after "Invoke Gradle" step.
The following command will create a .png image file of the firefox screenshot (only one page screenshot) and assuming xvfb is running on DISPLAY=:107
xwd -root -display :107 | convert xwd:- /tmp/capture2.png
and the following xvfb process (which is still running, containing a valid Xvfb_screen**** file in it - which was created by the Jenkins job where Xvfb plugin is configured with offset base 100 and 7 is the node/build number thus, making :107 as DISPLAY number).
u10002 30717 19950 1 01:16 ? 00:00:00 Xvfb :107 -screen 0 1024x768x8 -fbdir /production/JSlaves/kobaloki2_1/xvfb-2015-02-04_01-16-37-6170319257811815857.fbdir
I'm not running Xvfb / Imagemagick etc to just get an image of a URL (ex: www.google.com) but trying to get all the screenshots what a test is rendering behind Xvfb memory virtual framebuffer/file during the test run.
Are there any other tools (simple enough to install without messing up with the Linux server) which can achieve the same (capturing screenshots of all the pages that a test is rendering behind Xvf/firefox/Linux server in Headless way)?
I also tried Selenium Grid server, but FF is acting up there (due to some reason) thus I'm trying to run these tests using Jenkins, Gradle, Xvfb plugin on a Linux server (Headless mode) using firefox browser and planning to have N no. of executors to run multiple runs of these tests and finally capturing the results per run.
I'm archiving the artifacts (if any) and using Image Gallery plugin as well, but don't have the images for all the rendered pages which ran in Selenium behind Xvfb/firefox.
Any inputs are greatly appreciated.
Thanks.

If you're running with Selenium then you could use driver.getScreenshotAs()
http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp
Set this at the end of a step or method where you want a screenshot and output it to disc.

OK, this is what I did. This approach doesn't require any change to the source code of the project.
Installed imagemagick (..ck) i.e. yum install imagemagick on RHEL.
Created a script on the target server and it works now. All I do is, in the Jenkins job, when I have already started the Xvfb instance (using Xvfb plugin in Jenkins), then just a second before before running the Selenium GUI tests via Gradle (or any build tool), I call the following script and pass the parameters (where DISPLAY variable value is available to the Jenkins job as we are using Xvfb plugin in it). At the end of tests, the script exists automatically (as xwd command doesn't get any more input so it exits gracefully) and finally I publish the images and .mp4 (video) file on Jenkins (as a side bar link to show Test results / video) and archive the artifacts (.png image files using "Image Galary Plugin" and .mp4 file).
NOTE: This requires that your machine has: imagemagick, xwd and ffmpeg installed. If the options passed to any commands differs acc. to your OS machine, then tweak it accordingly. The framerate value in ffmpeg command can be a fraction i.e. 1/5 or 0.5 or 15 or anything you want (try it and see what you get).
It's up to you, if you want to ARCHIVE this big amount of data or not. You can do it if you have good space and if your Jenkins job have a better old build clean retention policies.
#!bin/bash
##
## This script will capture Screenshot (every 0.1 seconds) of an automated GUI (for ex: Selenium tests) tests running behind a HEADLESS Xvfb display instance.
## Then, it'll create a mp4 format movie using the captured screenshots.
##
## Machine where you run this script, should have: Xvfb service running, a session started by Xvfb plugin via Jenkins, xwd,ffmpeg OS commands and imagemagick (utilities).
## - For ex, try this on RHEL to install imagemagick: yum install imagemagick
##
## Variables
ws=$1; ## Workspace folder location
d=$2; d=$(echo $d | tr -d ':'); ## Display number associated with the Xvfb instance started by Xvfb plugin from a Jenkins job
wscapdir=${ws}/capturebrowserss; ## Workspace capture browser's screen shot folder
if [[ -n $3 ]]; then wscapdir=${wscapdir}/$3; fi ## If a user pass a 3rd parameter i.e. a Jenkins BUILD_NUMBER, then create a child directory with that name to archive that specific run.
i=1;
rm -fr ${wscapdir} 2>/dev/null || ( echo - Oh Oh.. Cant remove ${wscapdir} folder; echo -e "-- Still exiting gracefully! \n"; exit 0);
mkdir -p ${wscapdir}
while : ; do
xwd -root -display :$d 2>/dev/null | convert xwd:- ${wscapdir}/capFile_${d}_dispId`printf "%08d" $i`.png 2>/dev/null;
if [[ ${PIPESTATUS[0]} -gt 0 || ${PIPESTATUS[1]} -gt 0 ]]; then echo -e "\n-- Something bad happened during xwd or imagemagick convert command, manually check it.\n"; exit 0; fi
((i++)); sleep 0.1;
done
ffmpeg -r 5 -i ${wscapdir}/capFile_dispId_%08d.png ${wscapdir}/out_byRateOf5.mp4 2>/dev/null || echo -e "\n-- Some error occurred (may be too many files opened), exiting gracefully!\n";

Related

How to create a Linux GUI app short cut for WSL2 on Windows10?

I have properly installed and setup WSL2. It works fine.
I also setup X11 forwarding and X server (VcXsrv). I can launch GUI apps such like konsole or gvim or even google-chrome from a bash shell.
Now I want to launch konsole by simply double clicking a short cut on the desktop without launching the bash command mode terminal. How should I do it?
I tried running this in cmd:
> wsl /usr/bin/konsole
and it reports:
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, xcb.
I'm guessing it is because some X11 forwarding configurations were not properly setup, so I created a k.sh as follows:
#!/usr/bin/bash
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
export LIBGL_ALWAYS_INDIRECT=1
/usr/bin/konsole &
The first two lines were the X11 settings in my .bashrc, the last line launches konsole.
It works fine under bash environment; but when I ran
wsl k.sh
from windows cmd environment, it silently quitted without launching the konsole.
I'm out of ideas. What should I do to directly launch konsole or other Linux GUI apps under windows without having to getting into bash?
Thanks in advance.
You are asking about two different command-lines, and while the failures in running them via the wsl command have the same root-cause, the underlying failures are likely slightly different.
In both cases, the wsl <command> invocation results in a non-login, non-interactive shell where the command simply "runs and exits".
Since the shell is non-login/non-interactive, your startup files (such as ~/.bashrc and ~/.bash_profile, among others) are not being processed.
When you run:
wsl /usr/bin/konsole
... the DISPLAY variable is not set, since, as you said, you normally set it in your ~/.bashrc.
Try using:
wsl -e bash -lic "/usr/bin/konsole"
That will force bash to run as a login (-l), interactive (-i) shell. The DISPLAY should be set correctly, and it should run konsole.
Note that the quotes probably aren't necessary in this case, but are useful for delineating the commands you are passing to bash. More complicated command-lines can be passed in via the quotes.
As for:
wsl k.sh
That's likely a similar problem. You are doing the right thing by setting DISPLAY in your script, but I notice that you aren't using a fully-qualified path it. This would normally work, of course, if your script is in a directory on the $PATH.
But I'm guessing that you might add that directory to the $PATH in your startup config, which means (again) that it isn't being set in this non-login, non-interactive shell.
As before, try:
wsl -e bash -lic "k.sh"`
You could also use a fully-qualified path, of course.
And, I'm fairly sure you are going to run into an issue with trying to put konsole in the background via the script. When WSL exits, and the bash shell process ends, the child konsole process will terminate as well.
You could get around this with a nohup in the script, but then you also need to redirect the stderr. It's probably easiest just to move the & from the script itself to the command-line. Change your k.sh to:
#!/usr/bin/bash
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
export LIBGL_ALWAYS_INDIRECT=1
/usr/bin/konsole
Then run it with:
wsl -e bash -lic "k.sh &"`
Finally, a side note that when and if you can upgrade to Windows 11, it will automatically create Windows Start Menu entries for any Linux GUI app you install that creates a .desktop file. You can manually create .desktop files to have WSL create Start menu items for most applications.
For reference, in Windows 11 it's easier. To run a GUI application without a terminal window popping up, you just need to call wslg.exe instead of wsl.exe.
So, for example:
target: C:\Windows\System32\wslg.exe konsole
start in: C:\WINDOWS\system32
shortcut key: None
comment: Konsole
This tutorial shows how to install VcXsrv and and edit .bashrc to ensure that the "DISPLAY env var is updated on every restart".
DISPLAY env var needs to be dynamic setting.
I've used it successfully with WSL2 on Windows10 Version 21H2 (OS build 19044.2130) to run Chrome, Edge, and thunar. I'm using the Ubuntu 20.04 Linux distro.
To edit .bashrc follow these instructions.

Python cron job with Chrome not running in AWS EC2

I've been using an EC2 instance to run a python script with cron everyday for a month or so. The script uses selenium.
Everything was working correctly until today, when my script did not run.
I have tried to run it manually but it's not working either. The error message says that
raise exception_class(message, screen, stacktrace) selenium.common.exceptions.
NoSuchElementException: Message: no such element: Unable to locate element:
{"method":"cssselector","selector":"#ctl00_ctl00_moteurRapideOffre_
ctl01_EngineCriteriaCollection_Contract > option:nth-child(5)"}
(Session info: headless chrome=90.0.4430.85)
However, the same script is running fine on my computer (ie on my Macbook, not on AWS EC2).
As the problem seems to come from Chrome, I uninstalled it on AWS EC2 using:
sudo yum remove google-chrome-stable
Then I reinstalled it using :
curl https://intoli.com/install-google-chrome.sh | bash
sudo mv /usr/bin/google-chrome-stable /usr/bin/google-chrome
google-chrome --version && which google-chrome
If I try to run Chrome on the EC2 using /usr/bin/google-chrome, it does not work and it displays the following error message :
ERROR:browser_main_loop.cc(1386)] Unable to open X display.
I don't know if it was working before as I have never used it this way. But it seems to be a problem.
I have seen on the web that it might come from the fact that there is no screen and that I should use a package named xvfb. I have tried to install it with the following code:
sudo yum install xorg-x11-server-Xvfb
I guess the package was correclty installed, but it is not working better.
To sum up, I think my problem in the python code is linked to the fact that Google Chrome is not working correclty and this might be linked to xvfb. But I am not sure at all, it is just what I have tried until now.
Could you please help me ? Thanks!
You can simply add setup your like this, runs after every 30 minutes
*/30 * * * * export DISPLAY=:0 && ,<do what ever you want.>
If this does not work, and you google-chrome or firefox not found, simply run the command below in your shell BASH, FISH, ZSH etc to get PATH.
echo $PATH
Whatever the result comes out from the above command just copy and paste it above your cronjob like this,
*/30 * * * * export DISPLAY=:0 && ,<your selenium script.>```
You can remove export ```export DISPLAY=:0``` line if you want to this in the background or make your driver headless.
The reason of doing this, you might install the respective from snapd etc and that's why path is not defined as you downloaded from separate resource.

Xvfb Jenkins plugin: Unrecognized option: -displayfd

Jenkins version: 1.573
Jenkins Xvfb Plugin version: 1.0.15 (latest)
Linux OS: Red Hat Enterprise Linux Server release 5.9 (Tikanga)
Xorg -version
X Window System Version 7.1.1
Release Date: 12 May 2006
X Protocol Version 11, Revision 0, Release 7.1.1
Build Operating System: Linux 2.6.18-308.13.1.el5 x86_64 Red Hat, Inc.
Current Operating System: Linux kobaloki2 2.6.18-348.16.1.el5 #1 SMP Sat Jul 27 01:05:23 EDT 2013 x86_64
Build Date: 06 November 2012
Build ID: xorg-x11-server 1.1.1-48.100.el5
Before reporting problems, check http://wiki.x.org
to make sure that you have the latest version.
Module Loader present
which Xvfb
/usr/bin/Xvfb
I have some Selenium GUI based tests, that I'm running against a given environment/server's web site and where these tests check if everything for that site is working fine or not i.e. performing logging in / out and some other few clicks here n there successfully.
As these are Selenium GUI tests and I want to run these tests on a machine (Linux) in a HEADLESS mode, I need X display server (Xvfb).
I exported DISPLAY variable and started /etc/init.d/xvfb successfully.
root 5996 1 0 2014 ? 00:00:00 /usr/bin/Xvfb :99 -ac -screen 0 1024x768x8
I'm using Xvfb plugin, which is installed successfully on my Jenkins instance and configurations in both Jenkins Global and Jenkins job level is setup correctly and it's working fine if I run the job on master/slave instances (NOTE: Currently I have created 2 slaves on the same master server but I have other separate servers where I'm planning to install more slaves).
When I run only 2 simultaneous runs of the job, I see the following additional processes i.e. per run and the job finishes successfully. NOTE: My offset value in Xvfb plugin is 1. If I use 100, then the following will show :101 and :102 respectively.
u10003 16264 6921 1 12:56 ? 00:00:01 Xvfb :1 -screen 0 1024x768x8 -fbdir /production/JSlaves/kobaloki2_2/xvfb-2015-02-03_12-56-41-60597.fbdir
u10003 16289 6691 0 12:56 ? 00:00:00 Xvfb :2 -screen 0 1024x768x8 -fbdir /production/JSlaves/kobaloki2_1/xvfb-2015-02-03_12-56-46-7546741396559175462.fbdir
I trying to run concurrent runs of a Jenkins job (which successfully runs Selenium GUI Integration/Acceptance tests on a master / slave servers).
Now, What I'm trying to achieve is to run multiple concurrent builds/runs of this Jenkins job (so that I can have multiple tests running at the same time i.e. to perform some kind of Volume based testing). At this moment, I don't want to run these tests on a Selenium Grid server (out of scope of this post).
My questions:
1. If the check box for "Let Xvfb choose display name" is checked, then I'm getting the following error (here the job ran on a master Jenkins instance instead of a slave, thus /production/jenkinsAKS/... base folder). How can I make Xvfb to use -displayfd variable successfully?
13:33:01 Xvfb starting$ Xvfb -displayfd 2 -screen 0 1024x768x8 -fbdir /production/jenkinsAKS/xvfb-2015-02-03_13-33-00-6577455998897275731.fbdir
13:33:01 Unrecognized option: -displayfd
...
....bunch of options for Xvfb command
...
..
13:33:01 Fatal server error:
13:33:01 Unrecognized option: -displayfd
13:33:01
13:33:11
13:33:11 ERROR: Xvfb failed to start, consult the lines above for errors
Per this link: https://wiki.jenkins-ci.org/display/JENKINS/Xvfb+Plugin
Let Xvfb choose display name Uses the -displayfd option of Xvfb by which it chooses its own display name by scanning for an available one. This option requires a recent version of xserver, check your installation for support. Useful if you do not want to manage display number ranges but have the first free display number be used.
2. In the above snapshot (Xvfb plugin), I see Xvfb additional options box, is there any option that I can try which will tell Xvfb to use a display# which is not currently in use?
3. It seems like I need to update X server version (Xorg -version). How can I do that, what commands should I run?
4. If I un-check the above mentioned checkbox and if I run multiple builds (more than 2) of this Jenkins job, then I get the following error if the DISPLAY number is already in use. Using that checkbox in Xvfb plugin, I was trying to tell Xvfb to use the display number from the free list if one if not available.
This error comes either for display #1 or #2 depending upon how Xvfb plugin assigns the number in Jenkins environment (using node/slave# etc).
13:04:27 Fatal server error:
13:04:27 Server is already active for display 1
13:04:27 If this server is no longer running, remove /tmp/.X1-lock
13:04:27 and start again.
13:04:27
13:04:27 unlink: No such file or directory
13:04:42 unlink failed, errno 2
13:04:42 ERROR: Xvfb failed to start, consult the lines above for errors
**How can I get rid of the above error** (seems like when I can resolve bullet 2 above)?
NOTE: If I use a single slave (either on the same master instance or on any other server) and increase the # of executors from 1/2 to 20 or greater, then, Xvfb is successfully running N number of builds/runs/tests at the same time without any failures. I can also use naginator plugin if required for retrying a failed build if any due to DISPLAY not available. BUT, this is not what I'm looking at this time.
Answer time.
It depends on your machine i.e. Xvfb installed on your machine may not have -displayfd option (but may be a different similar one) but Xvfb plugin in Jenkins is passing it for you when you check that checkbox. Try a different option if available (see Xvfb help or man page on your OS machine). Now, I'm NOT using / checking this checkbox.
Actually not required as Xvfb plugin will generate a new instance and assign a DISPLAY (:NN) where NN is a number automatically per individual run.
I can use yum command.
This error doesn't come each time. If this happens and error comes in all Jenkins job runs, then you can run the following command to fix it.
/etc/init.d/xvfb stop; sleep 2; /etc/init.d/xvfb start
To get a copy of xvfb file, you can get it online (where some xvfb file which sits under /etc/init.d folder, have more options that just stop/start.
Now, the solution to my ACTUAL problem (for which I was trying everything) is mentioned in other post here: Xvfb, Jenkins, Selenium tests - Capture Screenshots of all pages

Jenkins & TestNG start browsers

Is it possible to make Jenkins use actual browser instead of headless browser? I a running some tests written in TestNG (using Selenium webdriver). When I run the testng.xml file in Eclipse, the browser starts and the tests run. But when I use Jenkins and run the tests with maven, it doesn't start any browsers.
If your jenkins is hosted in a Windows machine, there are some special configurations you should know about services that are allow to use the interface.
By the way, the easiest way to see the browsers running is starting jenkins using the command line:
java -jar jenkins.war
In linux you could use the same command or use xvfb plugin to run browsers in background.
Hope helps
In addition to this, the main reason for not launching the browser is JNLP (java network launch protocol) , when we execute the war we can interact with the desktop applications.
Using Selenium Grid will allow you to execute the test on Jenkins but open the browser on a remote slave.
To achieve this you need to create an instance RemoteWebdriver than ChromeDriver, IEDriver etc I
For linux. If jenkins is running as a daemon, you could specify active display to connect to and run your browser on it. Check what display you could connect to:
ps e | grep -Po " DISPLAY=[\.0-9A-Za-z:]* " | sort -u
My output is:
DISPLAY=:10.0
DISPLAY=:2
DISPLAY=:2.0
Then go to your jenkins project -> Configure -> Build and add the next string above your main build configuration through "Add build step -> Execute shell"
/bin/bash -c "export DISPLAY=:10"
Edited: I've encountered the issue again recently. To resolve it:
I've given for the jenkins' user ability to interact with the desktop of my current user:
xhost +si:localuser:jenkins
so if I connect to my linux system through ssh using jenkins' user credentials, export display of my current user (export DISPLAY=:10) and run, for example google-chrome or firefox inside of putty's terminal, they are launching on my current user's desktop.
After this I've checked If I could start "mvn test" command inside of workspace/MyTests folder from my putty so it will start browser and execute tests.
At the end I've created simple script in the root of my current user:
vi ~/.startup.sh
#!/bin/bash
xhost +si:localuser:jenkins
and added it to my Xfce4 GUI: Application -> Settings -> Session and Startup -> Application Autostart, specifying command field as:
sh -c $HOME/.startup.sh
It's because of this script should work only when desktop is loaded to share my current user's display with jenkins' user. After reboot and connecting to this server through RDC desktop loads with xhost command applied. And after this jenkins could interract with desktop even when I close the RDP connection but leaving current user's session alive.
I've removed Build step in my jenkins' project configuration that was stated "Invoke top-level Maven targets". It could not start my browsers.
I've changed my "Add build step -> Execute shell" to:
export DISPLAY=:10
cd /var/lib/jenkins/workspace/MyTests
mvn test
I've tried grid also, turning selenium-server-standalone -hub and -node into daemons. But it was slower than launching browsers with WebDriver in the such way.

Setting up JS debugging with IntelliJ/WebStorm and PhantomJS/Casper

Can I get an interactive JS debugger working on PhantomJS and/or CasperJS?
I didn't solve this entirely, but I definitely reduced the pain.
PhantomJS provides a command line argument to enable webkit's remote debugger. AFAIK, PhantomJS launches a server and dumps the script into the <head> of a webpage with the familiar in-browser debugger. It's actually pretty nice, with breakpoints, etc. However, switching to manually digging around in the terminal for a random command line parameter and the path to your script is seriously irritating.
So, I used IntelliJ's "external tools" feature to launch a Bash script that kills any previous debugging session, launches PhantomJS, and then opens the page up in Chrome.
#!/bin/bash
lsof -i tcp#0.0.0.0:9000 #list anything bound to port 9000
if [ $? -eq 0 ] #if something was listed
then
killall 'phantomjs'
fi
/usr/local/Cellar/phantomjs/2.0.0/bin/phantomjs --remote-debugger-port=9000 $1 &
# --remote-debugger-autorun=yes <- use if you have added 'debugger;' break points
# replace $1 with full path if you don't pass it as a variable.
sleep 2; #give phantomJS time to get started
open -a /Applications/Google\ Chrome.app http://localhost:9000 & #linux has a different 'open' command
# alt URL if you want to skip the page listing
# http://localhost:9000/webkit/inspector/inspector.html?page=1
#see also
#github.com/ariya/phantomjs/wiki/Troubleshooting
The next few lines are settings for IntelliJ, although the above code works just as well on any platform/IDE.
program: $ProjectFileDir$/path/to/bash/script.sh
parameters: $FilePath$
working dir: $ProjectFileDir$
PhantomJS has a remote-debugger-port option you can use to debug your casper script in Chrome dev tools. To use it, simply execute your casper script with this argument:
casperjs test script.js --remote-debugger-port=9000
Then, open up http://localhost:9000 in Chrome and click on the about:blank link that presents itself. You should then find yourself in familiar Chrome dev tools territory.
Since this is a script and not a web page, in order to start debugging, you have to do one of two things before your script will execute:
In the Chrome dev tools page, open the console and execute __run() to actually start your script.
Insert a debugger; line in your code, and run your casper script with an additional --remote-debugger-autorun=yes argument. Doing so with the remote debug page open will run the script until it hits your debugger; line.
There's a great tutorial that explains this all very nicely.