Golang - Selenium - check if the element disappears - selenium

I'm trying to switch from Python to GO and faced with difficulties of detecting when an element disappears. The selenium.WebDriver has FindElement, which returns an error when an element couldn't be found and, at the same time, can return a connection error. Is it a legit way just to check the text of the error? Or is there any better way to find out when the element becomes invisible/disappears from the page?

To check if an element has disappeared from a web page in Go using Selenium, you can use a loop with a timeout to periodically check if the element is still present on the page. Here's an example:
package main
import (
"time"
"github.com/tebeka/selenium"
)
func main() {
// Connect to a running instance of Selenium WebDriver
webDriver, err := selenium.NewRemote(selenium.Capabilities{
"browserName": "chrome",
}, "http://localhost:4444/wd/hub")
if err != nil {
println(err)
return
}
defer webDriver.Quit()
// Load a webpage
webDriver.Get("https://www.example.com")
// Find the element you want to check for
element, err := webDriver.FindElement(selenium.ByID, "element_id")
if err != nil {
println("Element not found")
return
}
// Use a loop with a timeout to check if the element is still present
timeout := time.After(5 * time.Second)
for {
select {
case <-timeout:
println("Timeout reached, element may have disappeared")
return
default:
_, err := webDriver.FindElement(selenium.ByID, "element_id")
if err != nil {
println("Element has disappeared")
return
}
time.Sleep(500 * time.Millisecond)
}
}
}
In this way, the function webDriver.FindElement is used in a loop with a timeout of 5 seconds. The timeout channel is used to stop the loop after 5 seconds if the element has not disappeared. If the element disappears, webDriver.FindElement will return an error, and the loop will stop. The time.Sleep function is used to wait between checks to avoid excessive CPU usage. You can adjust the sleep time as needed.

Related

WebDriverWait times out when run through Continuous Integration

I use the following code to check if an element is visible for my automated tests before interacting with them (Selenium/C#).
public bool ElementVisible(IWebDriver driver, IWebElement element, int secondsToWait = 30, bool doNotFailTest = false)
{
try
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(secondsToWait));
wait.Until<IWebElement>(d =>
{
if (element.GetCssValue("display") != "none" && element.Size.Height > 0 && element.Size.Width > 0)
{
return element;
}
return null;
});
return true;
}
catch (Exception)
{
if (!doNotFailTest)
{
throw;
}
return false;
}
}
The tests that use this method work every time, when I run tests on my PC. However, when I trigger the tests to be run on our build machine from TFS's Continuous Integration, only then does this method time out when called by my tests. Another point that might be worth noting is: this method works on other websites we test (both locally and through the CI). Just not this one website for some reason...
I have tried:
Running the tests locally on the build machine, cutting out the CI = no issue.
Increased the time out several times to greater values = method times out at the greater values.
Added Thread.Sleep wait before the above try/catch block (my reasoning for this stems from an issue I found on Browserstack whereby an AJAX injected element would not be found by this method alone without first adding an arbitrary wait before the WebDriverWait... which doesn't make sense to me because WebDriverWait is apparently the only thing I need to use to find AJAX injected elements (from what I've read anyway)).
As an aside, the reason I've made this method a bool is because not all of my tests should fail if certain elements aren't found. For example, our website rarely has the terms and conditions updated. When it does, a modal is presented to the user when logging in. We've left this as a manual test, but to prevent this modal from breaking the daily runs, we look for it and accept the terms if it's there (suppressing exceptions).
This timeout error relates to an element of the test we do actually want to find, it is the condition to pass our LogIn test by finding an element on our website that is only present when logged in. This element is injected with AJAX.
Better colleagues than me at CI can't pinpoint why this issue is occurring. Theoretically, the trigger from the CI is simply to initiate the test run - it has no other involvement in the running of the tests...?
I have observed that when triggering the test run from the CI to the build machine, when I VPN to the build machine, I expect to see the browser load up and the tests being conducted, but this is not the case. Maybe this is a factor? Perhaps I'm wrong but this behaviour seems like the tests are running on a headless browser? Yet we have not specified any settings to use a headless version of Chrome (v75).
If it was the case that testing through a headless browser throws timeout errors related to AJAX elements, other tests have passed after logging in - using other AJAX injected elements. It is only when any test calls this method, does it time out, when the run is triggered from the CI, on this specific website.
Very confusing!
I created a JavaScript version of the WebDriverWait, added some debugging (Console.Error.WriteLine()) to output the element's computed styles. The display property was 'none'... however, when I log in manually, in Chrome's developer tools, it is 'block'. Weird.
The element I was looking for was a DIV container that has the class 'loggedin'. I instead passed through an element inside this container that would also only be displayed when logged in (a piece of text). This worked.
My WebDriverWait doesn't work with either of the elements, so I have to stick with this JavaScript alternative. One thing to note, however, is that the evaluation of the element's properties takes 5 seconds, so I've omitted setting an interval between checks.
public bool ElementVisible(IWebDriver driver, By locator, int numberOfPolls = 10, bool doNotFailTest = false)
{
bool elementVisible = false;
int pollCount = numberOfPolls;
Thread.Sleep(1000);
while (!elementVisible && pollCount > 0)
{
try
{
// Takes 5 seconds to compute, bear this in mind when setting an interval or increasing the pollCount
elementVisible = (bool)js.ExecuteScript("var display = window.getComputedStyle(arguments[0]).display; var height = window.getComputedStyle(arguments[0]).height; var width = window.getComputedStyle(arguments[0]).width; return (display != 'none' && height != 0 && width != 0);", driver.FindElement(locator));
}
catch (Exception)
{
}
if (elementVisible)
{
break;
}
pollCount--;
}
if (!elementVisible)
{
if (!doNotFailTest)
{
throw new Exception("ElementVisible(): - element not visible.");
}
}
return elementVisible;
}

conditionally running tests with build flags not working

I'm running some tests in golang and I want to avoid running the slow ones, for example this one uses bcrypt so it's slow:
// +build slow
package services
import (
"testing"
"testing/quick"
)
// using bcrypt takes too much time, reduce the number of iterations.
var config = &quick.Config{MaxCount: 20}
func TestSignaturesAreSame(t *testing.T) {
same := func(simple string) bool {
result, err := Encrypt(simple)
success := err == nil && ComparePassWithHash(simple, result)
return success
}
if err := quick.Check(same, config); err != nil {
t.Error(err)
}
}
To avoid running this in every iteration I've set up the // +build slow flag. This should only run when doing go test -tags slow but unfortunately it's running every time (the -v flag shows it's running).
Any idea what's wrong?
Your // +build slow needs to be followed by a blank line
To distinguish build constraints from package documentation, a series of build constraints must be followed by a blank line.
visit Build Constraints

golang restarted parent process doesn't receive SIGINT

I'm writing a little program to manage restarts to other processes.
Basically when an app process starts (call it A), it spawns a new process (call it D), which has a simple HTTP server. When D receives an http request, it kills A and restarts it.
Problem is, A now doesn't respond to CTRL-C, and I'm not sure why. It may be something simple or maybe I don't really understand the relationship between processes, the terminal, and signals. But it's running in the same terminal with the same stdin/stdout/stderr. Below is a full program demonstrating this behaviour.
package main
import (
"flag"
"log"
"net/http"
"os"
"os/exec"
"strconv"
"time"
)
/*
Running this program starts an app (repeatdly prints 'hi') and spawns a new process running a simple HTTP server
When the server receives a request, it kills the other process and restarts it.
All three processes use the same stdin/stdout/stderr.
The restarted process does not respond to CTRL-C :(
*/
var serv = flag.Bool("serv", false, "run server")
// run the app or run the server
func main() {
flag.Parse()
if *serv {
runServer()
} else {
runApp()
}
}
// handle request to server
// url should contain pid of process to restart
func handler(w http.ResponseWriter, r *http.Request) {
pid, err := strconv.Atoi(r.URL.Path[1:])
if err != nil {
log.Println("send a number...")
}
// find the process
proc, err := os.FindProcess(pid)
if err != nil {
log.Println("can't find proc", pid)
return
}
// terminate the process
log.Println("Terminating the process...")
err = proc.Signal(os.Interrupt)
if err != nil {
log.Println("failed to signal interupt")
return
}
// restart the process
cmd := exec.Command("restarter")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Println("Failed to restart app")
return
}
log.Println("Process restarted")
}
// run the server.
// this will only work the first time and that's fine
func runServer() {
http.HandleFunc("/", handler)
if err := http.ListenAndServe(":9999", nil); err != nil {
log.Println(err)
}
}
// the app prints 'hi' in a loop
// but first it spawns a child process which runs the server
func runApp() {
cmd := exec.Command("restarter", "-serv")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Println(err)
}
log.Println("This is my process. It goes like this")
log.Println("PID:", os.Getpid())
for {
time.Sleep(time.Second)
log.Println("hi again")
}
}
The program expects to be installed. For convenience you can fetch it with go get github.com/ebuchman/restarter.
Run the program with restarter. It should print its process id. Then curl http://localhost:9999/<procid> to initiate the restart. The new process will now not respond to CTRL-C. Why? What am I missing?
This doesn't really have anything to do with Go. You start process A from your terminal shell. Process A starts process D (not sure what happened to B, but never mind). Process D kills process A. Now your shell sees that the process it started has exited, so the shell prepares to listen to another command. Process D starts another copy of process A, but the shell doesn't know anything about it. When you type ^C, the shell will handle it. If you run another program, the shell will arrange so that ^C goes to that program. The shell knows nothing about your copy of process A, so it's never going to direct a ^C to that process.
You can check out the approach taken by two http server frameworks in order to listen and intercept signals (including SIGINT, or even SIGTERM)
kornel661/nserv, where the ZeroDowntime-example/server.go uses a channel:
// catch signals:
signals := make(chan os.Signal)
signal.Notify(signals, os.Interrupt, os.Kill)
zenazn/goji, where graceful/signal.go uses a similar approach:
var stdSignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}
var sigchan = make(chan os.Signal, 1)
func init() {
go waitForSignal()
}

any waitForJs function to wait for some javascript code returns true

This is question about golang selenium webdriver.
Is there any function that returns only after some js code return true.
var session *webdriver.Session
...
session.waitForJs(`$('#redButton').css('color')=='red'`)
// next code should be executed only after `#redButton` becomes red
The problem is that method session.waitForJs do not exist.
I don't see any wait functions in the golang bindings to Selenium, so you'll most likely need to define your own. This is my first attempt at golang, so bear with me:
type elementCondition func(e WebElement) bool
// Function returns once timeout has expired or the element condition is true
func (e WebElement) WaitForCondition(fn elementCondition, int timeOut) {
// Loop if the element condition is not true
for i:= 0; !elementCondition(e) && i < timeOut; i++ {
time.sleep(1000)
}
}
There are two options to define the elementCondition. Your method of using Javascript looks like it could work with the ExecuteScript function documented in webdriver.go
// Inject a snippet of JavaScript into the page for execution in the
context of the currently selected frame. The executed script is
assumed to be synchronous and the result of evaluating the script is
returned to the client.
An alternative is to access the element properties through Selenium
func ButtonIsRed(WebElement e) (bool) {
return (e.GetCssProperty('color') == 'red')
}
So your code would become
var session *webdriver.Session
....
// Locate the button with a css selector
var webElement := session.FindElement(CSS_Selector, '#redButton')
// Wait for the button to be red
webElement.WaitForCondition(ButtonIsRed, 10)

Exit with error code in go?

What's the idiomatic way to exit a program with some error code?
The documentation for Exit says "The program terminates immediately; deferred functions are not run.", and log.Fatal just calls Exit. For things that aren't heinous errors, terminating the program without running deferred functions seems extreme.
Am I supposed to pass around some state that indicate that there's been an error, and then call Exit(1) at some point where I know that I can exit safely, with all deferred functions having been run?
I do something along these lines in most of my real main packages, so that the return err convention is adopted as soon as possible, and has a proper termination:
func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
func run() error {
err := something()
if err != nil {
return err
}
// etc
}
In Python I commonly use a pattern, which being converted to Go looks like this:
func run() int {
// here goes
// the code
return 1
}
func main() {
os.Exit(run())
}
I think the most clear way to do it is to set the exitCode at the top of main, then defer closing as the next step. That lets you change exitCode anywhere in main, and it's last value will be exited with:
package main
import (
"fmt"
"os"
)
func main() {
exitCode := 0
defer func() { os.Exit(exitCode) }()
// Do whatever, including deferring more functions
defer func() {
fmt.Printf("Do some cleanup\n")
}()
func() {
fmt.Printf("Do some work\n")
}()
// But let's say something went wrong
exitCode = 1
// Do even more work/cleanup if you want
// At the end, os.Exit will be called with the last value of exitCode
}
Output:
Do some work
Do some cleanup
Program exited: status 1.
Go Playgroundhttps://play.golang.org/p/AMUR4m_A9Dw
Note that an important disadvantage of this is that you don't exit the process as soon as you set the error code.
As mentioned by fas, you have func Exit(exitcode int) from the os package.
However, if you need the defered function to be applied, you always can use the defer keyword like this:
http://play.golang.org/p/U-hAS88Ug4
You perform all your operation, affect a error variable and at the very end, when everything is cleaned up, you can exit safely.
Otherwise, you could also use panic/recover:
http://play.golang.org/p/903e76GnQ-
When you have an error, you panic, end you cleanup where you catch (recover) it.
Yes, actually. The os package provides this.
package main
import "os"
func main() {
os.Exit(1)
}
http://golang.org/pkg/os/#Exit
Edit: so it looks like you know of Exit. This article gives an overview of Panic which will let deferred functions run before returning. Using this in conjunction with an exit may be what you're looking for. http://blog.golang.org/defer-panic-and-recover
Another good way I follow is:
if err != nil {
// log.Fatal will print the error message and will internally call System.exit(1) so the program will terminate
log.Fatal("fatal error message")
}