I use Selenium with Phantomjs, and want to get the page content after the page fully loaded.
I tried http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp but it seems not working with phantomjs
Explicit wait:
using (IWebDriver driver = new PhantomJSDriver())
{
IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
driver.Navigate().GoToUrl(url);
content = driver.PageSource;
driver.Quit();
}
Another test:
using (IWebDriver driver = new PhantomJSDriver())
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
driver.Url = url;
IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("footer")); // failed because it's not yet loaded full content
});
content = driver.PageSource;
}
Or implicit wait:
using (IWebDriver driver = new PhantomJSDriver())
{
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(30));
driver.Navigate().GoToUrl(url);
content = driver.PageSource;
driver.Quit();
}
The content is still lacking. The only way is to put Thread.Sleep(waitTime); which is not a good solution for this.
Thanks.
For your "Explicit wait:" option, I think the correct sequence should be:
1) Navigate to target url by:
driver.Navigate().GoToUrl(url);
2) Wait until the target url fully loaded by
IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
In this way next line will wait page fully loaded before read PageSource.
I have created a extension method. In this method you can put your condition.
public static bool WaitUntil(this IWebDriver driver, Func<IWebDriver, bool> expression, int timeOutSeconds = 10)
{
TimeSpan timeSpent = new TimeSpan();
bool result = false;
while (timeSpent.TotalSeconds < timeOutSeconds)
{
result = expression.Invoke(driver);
if (result == true)
{
break;
}
Thread.Sleep(timeSleepingSpan);
timeSpent = timeSpent.Add(new TimeSpan(0, 0, 0, 0, timeWaitingSpan));
}
return result;
}
Like
driver.WaitUntil(d => d.Url.Equals("https://www.meusite.com/"));
Try something like this:
try (
ExpectedConditions.presenceOfElementLocatedBy
ExpectedConditions.visibilityOfElementLocatedBy
) catch error if both conditions are not met
Related
I have problem getting output from html2canvas JS library within Chrome automated by selenium.
Response is always null, but I can see in the Chrome console that code executed successfully and screenshot has been encoded
public byte[] TakeScreenshot(string fileName)
{
this.logger.Debug($"Taking screenshot");
var seleniumDownloadPath = this.seleniumEngine.GetDownloadPath();
IJavaScriptExecutor js = Driver as IJavaScriptExecutor;
var html2canvasJs = System.IO.File.ReadAllText(Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), this.seleniumEngine.GetHtml2CanvasPath()));
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(60));
var response = new object { };
js.ExecuteScript(html2canvasJs);
string generateScreenshotJS =
var canvasImgContentDecoded;
#"function genScreenshot () {
html2canvas(document.body).then(function(canvas) {
window.canvasImgContentDecoded = canvas.toDataURL(""image/png"");
console.log(window.canvasImgContentDecoded);
return window.canvasImgContentDecoded;
});
}
genScreenshot();";
response = js.ExecuteScript(generateScreenshotJS);
}
I also tried solution from this Here but the behavior was unstable (e.g. when running realtime i got error of nulls, but if running using breakpoints, I got result sometime)
Finally found the solution after finding out that execution of script takes more time and variable was null. So added wait function to retrieve once its filled - window.canvasImgContentDecoded
public byte[] TakeScreenshot(string fileName)
{
this.logger.Debug($"Taking screenshot");
var seleniumDownloadPath = this.seleniumEngine.GetDownloadPath();
IJavaScriptExecutor js = Driver as IJavaScriptExecutor;
var html2canvasJs = System.IO.File.ReadAllText(Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), this.seleniumEngine.GetHtml2CanvasPath()));
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(60));
var response = new object { };
js.ExecuteScript(html2canvasJs);
string generateScreenshotJS =
#"
var canvasImgContentDecoded;
function genScreenshot () {
html2canvas(document.body).then(function(canvas) {
window.canvasImgContentDecoded = canvas.toDataURL(""image/png"");
console.log(window.canvasImgContentDecoded);
});
}
genScreenshot();";
response = js.ExecuteScript(generateScreenshotJS);
string getSCreenShot = "return window.canvasImgContentDecoded;";
var encodedPngContent = new object { };
/*ADDED WAIT FUNCTION*/
wait.Until(
wd =>
{
encodedPngContent = js.ExecuteScript(getSCreenShot);
if (encodedPngContent != null)
{
return true;
}
return false;
});
string pngContent = encodedPngContent.ToString();
pngContent = pngContent.Replace("data:image/png;base64,", string.Empty);
string fileSavePath = this.seleniumEngine.GetDownloadPath() + fileName;
File.WriteAllBytes(fileSavePath, Convert.FromBase64String(pngContent));
byte[] fileByte = System.IO.File.ReadAllBytes(fileSavePath);
File.Delete(fileSavePath);
return fileByte;
}
Keep getting this exception at random intervals. For instance, the run will be okay now, but if i re run again in few mins I see the exception. Many have suggested to downgrade to selenium version 3.3.0 but i don't want to do that. Is there a workaround? I am currently using Selenium 3.14.0.
`
public void ATestCase2()
{
try
{
var category = driver.FindElement(By.Id("lRunMode"));
var selectElement = new SelectElement(category);
selectElement.SelectByValue("Pass");
var wait = new WebDriverWait(driver, new TimeSpan(0, 0, 30));
var SerachBy = driver.FindElement(By.Id("ddlSechBy"));
var SearchByPaidLoss = new SelectElement(SerachBy);
SearchByPaidLoss.SelectByValue("By Wim");
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.Id("tbxrchValue")));
IWebElement SerachTextBox = driver.FindElement(By.Id("tbxSearValue"));
SerachTextBox.Clear();
string claimNumber2 = "" + "ACs-01";
SerachTextBox.SendKeys(claimNumber2);
IWebElement GoButton = driver.FindElement(By.Id("btnndClaim"));
GoButton.Click();
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.AlertIsPresent());
// Switch the control of 'driver' to the Alert from main Window
IAlert simpleAlert = driver.SwitchTo().Alert();
// '.Text' is used to get the text from the Alert
String alertText = simpleAlert.Text;
Console.WriteLine("Alert text is " + alertText);
// '.Accept()' is used to accept the alert '(click on the Ok button)'
simpleAlert.Accept();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
}
`
In part of my code, I get an error that says the code is unreachable.
In the second part, I get an error message that DesiredCapabilitie is obsolete.
How can I solve this problem?
My code is as follows:
public void CreateWebDriver()
{
if (false)
{
var pathToDriver = ConfigurationManager.AppSettings["PathToChromeDriverLocal"];
var chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("--disable-extensions");
WebDriver = new ChromeDriver(pathToDriver, chromeOptions);
}
else if (true)
{
var pathToDriver = ConfigurationManager.AppSettings["PathToChromeDriverRemote"];
DesiredCapabilities chromeCapabilities = DesiredCapabilities.Chrome();
chromeCapabilities.SetCapability("chrome.binary", #"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe");
WebDriver = new RemoteWebDriver(new Uri(pathToDriver), chromeCapabilities);
}
}
if(false){
// code unreachable because condition is always false
}
// You have to try like this
int a=5,b=10
if(a>b){ // do something
}
else if(a=b){ // do something
}
else { // do something
}
I know I can wait for an element to exist by doing this:
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(5));
var element = wait.Until(d => d.FindElement(By.Id("foo-status")));
If it doesn't exist within 5 seconds, element is null.
How do I get Selenium to wait for the element to have a certain text value?
(I have answered my own question, but would be keen for any better answers or improvements.)
An easier solution for your usecase to wait for the element to have a certain text can be implemented with the help of ExpectedConditions in-conjunction with the method TextToBePresentInElement as follows :
wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
var expectedText = "good";
var element = wait.Until(ExpectedConditions.TextToBePresentInElement(By.Id("foo-status"), expectedText));
The trick is to use a filter method that returns null if the text does not match.
public static IWebElement ElementTextFilter(IWebElement webElement, string text) {
if (webElement == null)
return null;
return webElement.Text.Equals(text, StringComparison.OrdinalIgnoreCase)
? webElement : null;
}
// ...
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(5));
var expectedText = "good";
var element = wait.Until(d =>
ElementTextFilter(d.FindElement(By.Id("foo-status")), expectedText)
);
Can easily turn this into an extension method which is a bit cleaner.
public static class WebElementExtensions {
public static IWebElement WithText(this IWebElement webElement, string text) {
if (webElement == null)
return null;
return webElement.Text.Equals(text, StringComparison.OrdinalIgnoreCase)
? webElement : null;
}
// ...
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(5));
var expectedText = "good";
var element = wait.Until(d => d.FindElement(By.Id("foo-status")).WithText(expectedText));
public static IWebElement FindElement(ExpectedConditions expectedConditions, By by, int timeoutInSeconds)
{
DefaultWait<IWebDriver> wait = new DefaultWait<IWebDriver>(driver);
wait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
wait.PollingInterval = TimeSpan.FromMilliseconds(10000);
wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
IWebElement element =
wait.Until<IWebElement>(ExpectedConditions.ElementIsVisible(by));
}
My questions:
How to put this expectedConditions instead of what is currently in my method ?
I try to change:
IWebElement element =
wait.Until<IWebElement>(ExpectedConditions.ElementIsVisible(by));
with this:
IWebElement element =
wait.Until<IWebElement>(expectedConditions(by));
And received this error:
Method name expected.
The Until method requires a predicate as first argument.
A predicate is a function that is called at a regular interval until it returns something different from null or false.
So in your case you need to make it return a predicate and not a IWebElement:
public static Func<IWebDriver, IWebElement> MyCondition(By locator) {
return (driver) => {
try {
var ele = driver.FindElement(locator);
return ele.Displayed ? ele : null;
} catch (StaleElementReferenceException){
return null;
}
};
}
// usage
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement element1 = wait.Until(MyCondition(By.Id("...")));
Which is equal to :
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement element = wait.Until(ExpectedConditions.ElementIsVisible(By.Id("...")));
element.Click();
You could also use a lambda expression
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement element = wait.Until((driver) => {
try {
var ele = driver.FindElement(By.Id("..."));
return ele.Displayed ? ele : null;
} catch (StaleElementReferenceException){
return null;
}
});
element.Click();
Or an extension method:
public static IWebElement WaitElementVisible(this IWebDriver driver, By by, int timeout = 10) {
return new WebDriverWait(driver, TimeSpan.FromSeconds(timeout)).Until((drv) => {
try {
var ele = drv.FindElement(by);
return ele.Displayed ? ele : null;
} catch (StaleElementReferenceException){
return null;
} catch (NotFoundException){
return null;
}
});
}
// usage
IWebElement element = driver.WaitElementVisible(By.Id("..."));
element.Click();
As you see, there is many ways to wait for an element is a specific state.