Selenium webdriver: How to check an img element really have height attribute - selenium

in java Selenium web driver on chrome, I need to make sure that an image has a height attribute. The HTML code I have is this:
<img src="https://www.w3schools.com/tags/img_girl.jpg">
and I'm trying to execute
boolean s = driver.findElement(By.tagName("img")).getAttribute("height") !=null;
but this always returns true although there's no height attribute! and if I try to execute
String s = driver.findElement(By.tagName("img")).getAttribute("height");
I'll get 600 despite there is no height attribute in the HTML code. is there any other way I can check it?

Try this example:
#Test
public void testHeightAttribute(){
driver.get("https://webelement.click/en/welcome");
WebElement img = driver.findElement(By.xpath("//img[1]"));
System.out.println(img.findElements(By.xpath(".[#height]")).size());
System.out.println(img.findElements(By.xpath(".[#src]")).size());
}
I am not sure this is the most elegant way though :). The idea is that you're looking up elements using the element itself as a search context (looking up element within itself). If no attribute is present in DOM then the list would be empty (size() == 0). Otherwise it would be 1.
Basically the common method would look like:
public boolean hasAttributeInDom(WebElement element, String attr){
return !element.findElements(By.xpath(".[#" + attr + "]")).isEmpty();
}

You can check height parameter in styles section of Developer tools. Refer to attached screen shot of sample image from Stack overflow. You can see height parameter at right hand side. Generally, dimension are mentioned here.
You will need to understand and look for particular image property in Styles section as its display CSS properties for entire page.

Related

Identifying a Customized Built-In Element without an is= attribute

In my Chessly.github.io project I use Customized Built-In IMG Elements to define SVG chesspieces:
Question: How can I distinguish a regular IMG from a customized IMG?
document.body.append(
document.createElement("IMG", {
is: "white-queen"
});
);
This creates a chesspiece, but does not set the is= attribute
I now explicitly set the is= attribute myself, but since this attribute does nothing and can be set to any value (I use is as an observed attribute in my Custom Element code) it is not a solid way to distinguish IMG elements from Customized IMG elements when walking the DOM.
If I promote a pawn (replace the img src)
<img is=white-pawn/>
with element.setAttribute("is","white-queen")
How can I determine the piece originally was the white pawn?
It still is a white-pawn in the Custom Elements registry.
Am I overlooking something?
Simplified code (with basic SVG shape) in JSFiddle: https://jsfiddle.net/dannye/k0va2j76/
Update: Code (based on correct answer below)
let isExtendedElement = x =>
Object.getPrototypeOf(x).__proto__.constructor.name !== 'HTMLElement';
note! This does NOT catch Autonomous Custom Elements!
maybe better:
let isBaseElement = x =>
Object.getPrototypeOf(Object.getPrototypeOf(x)).__proto__.constructor.name=='Element';
I think adding explicitly the is attribute is the best solution for now.
Else you'll have to deal with the class reference. In your case:
yourElement.constructor === customElements.get( 'circle-image' )
yourElement.constructor === CircleImage //if it's the named class
This supposes that you know the name of the custom elements you want to check.
Else you'll have to go through the protoype chain:
CircleImage --> HTMLImageElement --> HTMLElement --> Element --> Node
If HTMLElement is only the father, it's a built-in element.
If HTMLElement is the grandfather (or grand-grand...), it's probably an extended build-in element.
update
If you use a named class you can also retrieve its name:
elem.constructor.name

How to set height of WebView according to the length of content in Nativescript-Vue?

Update: I used HtmlView and yes, the height is dynamic with the content,
but it seems like not support <img />
I'm currently using WebView to render the markdown content as below
<WebView :src="marked_content" height="1200px" margin="20dp" />
The result is fixed at 1200px with scrollBar as expected, but what I actually want to do is to render the whole markdown content with various height and without scrollBar.
Anyone may help, please?
PS: Any other methods which may render markdown content is welcome! Thank you!
Do you have control over the website, if Yes you can do this
https://discourse.nativescript.org/t/dynamic-webview-height/4215/2?u=manojdcoder
I have worked out how to do this without plugins.
There is a solution above which appends the URL with a hash containing the page height. It didn't work for me because I'm adding HTML code directly.
For example
src="<p>blah blah</p>"
This is a plain JS solution, so you'll have to rework it to get it working in Vue / Typescript.
Give your WebView an id, do not set the height, and add the "loaded" and "loadFinished" handlers.
For the loaded handler.
platformModule = require("tns-core-modules/platform");
var webViewSrcObj = {};
exports.webViewLoaded = function(webargs){
if(platformModule.isAndroid){console.log("IS ANDROID!!!"); return false;}
webview = webargs.object;
if(webview.height == "auto"){
webViewSrcObj[webview.id] = webview.src;
webview.src += '<script>function getPageHeight(){if(document.documentElement.clientHeight>document.body.clientHeight){height = document.documentElement.clientHeight}else{height = document.body.clientHeight}; ph = document.getElementById("pageHeight"); window.location = "pageHeight.html?height="+height;} setTimeout(getPageHeight, 1);</script>';
}
}
It checks the platform, and returns false if it is Android (Android works fine already).
Then it checks if the height is set to "auto" (which is default).
if it is set to auto, it will copy the HTML content. I'll explain more about this later. It then appends the html with some JavaScript code that calculates the view height, and then redirects to an empty page. It does this in order to use the query string for the page height. Make sure that page exists in your app folder to avoid any page not found errors!
Then for the onLoadeFinished handler...
exports.webViewLoadFinished = function(webargs){
if(platformModule.isAndroid){
console.log("IS ANDROID!!!"); return false;
}
webview = webargs.object;
if(webargs.url.indexOf("?height") > -1){
height = (webargs.url).split("?height=");
height = height[1].substr(0, height[1].length)/1;
webview.height = height; webview.src = webViewSrcObj[webview.id];
}
}
This will check to see if the query string height value exists.
If so it uses the height value to set the height of the webview.
And finally it adds the HTML content that was copied in the onLoaded handler.
My initial testing works well even with multiple webviews in a page.
I haven’t done extensive testing, but it might help to increase the setTimeout time if you experience any problems.
If anyone is able to improve this solution, please share your results.

how to verify view is scrollable in mobile application (Automation)

Anyone knows automation script to verify a view (homePage/Browse) is scrollable or not. i can use ScrollTo(id) which is at the bottom of the page. But it is not a correct method to do, as test case passes if that element present in 1st page
Basically You cannot. You could try to cast the view to ScrollView class however any custom view can implement scroll.
Get the coordinates of any particular element like button etc unique element.
Swipe using driver.swipe() to 100 or more pixels.
And get the coordinates of that element again and check whether x or y coordinates changed or not.
This will let you know whether it is a single page application or more to scroll.
Basically there is no API to check the view is scrollable or not but if you still require this then you can do work around
#Test
public void testVerticalScroll()
{
//Try to Scroll till the 15th row
driver.scrollTo("List item:15");
//Assert that the 1st row is not visible.
Assert.assertFalse( driver.findElement(By.name("List item:01")).isDiaplyes())
//Assert that the 15th row is not visible.
Assert.assertTrue( driver.findElement(By.name("List item:15")).isDiaplyes())
}
You can consider the last visible element as "YourText" But this is
just a workaround that needs to be customized for each page.
Here we are using swipe until we find the element. In this case, the last visible element indicates the margin of the page.
Dimension dimensions = driver.manage().window().getSize();
Double screenHeightStart = dimensions.getHeight() * 0.5;
int scrollStart = screenHeightStart.intValue();
System.out.println("s="+scrollStart);
Double screenHeightEnd = dimensions.getHeight() * 0.2;
int scrollEnd = screenHeightEnd.intValue();
for (int i = 0; i < dimensions.getHeight(); i++) {
driver.swipe(0,scrollStart,0,scrollEnd,2000);
if (driver.findElement(By.name("YourText")).size()>0)
exit;
}
driver.findElement(By.name("YourText")).click();
There is a way to check it. You have to find a layer that you will target for example:
MobileElement scrollableLayer= driver.findElementById("elementID");
Then you will extract attribute value "scrollable" of that element like this:
String scrollableState = scrollableLayer.getAttribute("scrollable");
And then you can check if the String value is true or false.
if (scrollableState.equals("true")){System.out.println("it's scrolable"); }else{System.out.println("it's not scrolable");}
Or you can do whatever you want with it :)

Why element.text() != element.#text?

I'm new to Geb. I was trying to get text of html element. element.text() sometimes returns correct value, sometimes empty one. element.#text returns expected value.
Can someone explain this behaviour?
text() is for tag body and #text return the value of the text attribute
element.#text directly accesses the text attribute whereas text() calls a function to access the text, probably doing some stuff other than getting the attribute.
EDIT
According to the documentation, the text() method returns the text content for the first context element, which may be the reason why it sometimes doesn't return the value your are expecting (e.g. if you are looking for the text content for the second context element).
EDIT 2
For a NonEmptyNavigator, the implementation of the text() method looks like that:
#Override
String text() {
firstElement().text
}
For an EmptyNavigator, null is returned
#Override
String text() { null }
EDIT 3
As both Navigator implementations don't have a text attribute, the propertyMissing() method is called.
For implementation details, see here and here.

Clicking at coordinates without identifying element

As part of my Selenium test for a login function, I would like to click a button by identifying its coordinates and instructing Selenium to click at those coordinates. This would be done without identifying the element itself (via id, xpath, etc).
I understand there are other more efficient ways to run a click command, but I'm looking to specifically use this approach to best match the user experience. Thanks.
There is a way to do this. Using the ActionChains API you can move the mouse over a containing element, adjust by some offset (relative to the middle of that element) to put the "cursor" over the desired button (or other element), and then click at that location. Here's how to do it using webdriver in Python:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
browser = webdriver.Chrome()
elem = browser.find_element_by_selector(".some > selector")
ac = ActionChains(browser)
ac.move_to_element(elem).move_by_offset(x_offset, y_offset).click().perform()
Y'all are much to quick to dismiss the question. There are a number of reasons one might to need to click at a specific location, rather than on an element. In my case I have an SVG bar chart with an overlay element that catches all the clicks. I want to simulate a click over one of the bars, but since the overlay is there Selenium can't click on the element itself. This technique would also be valuable for imagemaps.
In C# API you use actions
var element = driver.FindElement(By...);
new Actions(driver).moveToElement(element).moveByOffset(dx, dy).click().perform();
Although it is best to just use simple Id, CSS, Xpath selectors when possible. But the functionality is there when needed (i.e. clicking elements in certain geographic places for functionality).
I first used the JavaScript code, it worked amazingly until a website did not click.
So I've found this solution:
First, import ActionChains for Python & active it:
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
To click on a specific point in your sessions use this:
actions.move_by_offset(X coordinates, Y coordinates).click().perform()
NOTE: The code above will only work if the mouse has not been touched, to reset the mouse coordinates use this:
actions.move_to_element_with_offset(driver.find_element_by_tag_name('body'), 0,0))
In Full:
actions.move_to_element_with_offset(driver.find_element_by_tag_name('body'), 0,0)
actions.move_by_offset(X coordinates, Y coordinates).click().perform()
This can be done using Actions class in java
Use following code -
new Actions(driver).moveByOffset(x coordinate, y coordinate).click().build().perform();
Note: Selenium 3 doesn't support Actions class for geckodriver
Also, note that x and y co-ordinates are relative values from current mouse position. Assuming mouse co-ordinates are at (0,0) to start with, if you want to use absolute values, you can perform the below action immediately after you clicked on it using the above code.
new Actions(driver).moveByOffset(-x coordinate, -y coordinate).perform();
If using a commercial add-on to Selenium is an option for you, this is possible: Suppose your button is at coordinates x=123, y=456. Then you can use Helium to click on the element at these coordinates as follows:
from helium.api import *
# Tell Helium about your WebDriver instance:
set_driver(driver)
click(Point(123, 456))
(I am one of Helium's authors.)
This worked for me in Java for clicking on coordinates irrespective on any elements.
Actions actions = new Actions(driver);
actions.moveToElement(driver.findElement(By.tagName("body")), 0, 0);
actions.moveByOffset(xCoordinate, yCoordinate).click().build().perform();
Second line of code will reset your cursor to the top left corner of the browser view and last line will click on the x,y coordinates provided as parameter.
In Selenium Java, you can try it using Javascript:
WebDriver driver = new ChromeDriver();
if (driver instanceof JavascriptExecutor) {
((JavascriptExecutor) driver).executeScript("el = document.elementFromPoint(x-cordinate, y-cordinate); el.click();");
}
Action chains can be a little finicky. You could also achieve this by executing javascript.
self.driver.execute_script('el = document.elementFromPoint(440, 120); el.click();')
I used the Actions Class like many listed above, but what I found helpful was if I need find a relative position from the element I used Firefox Add-On Measurit to get the relative coordinates.
For example:
IWebDriver driver = new FirefoxDriver();
driver.Url = #"https://scm.commerceinterface.com/accounts/login/?next=/remittance_center/";
var target = driver.FindElement(By.Id("loginAsEU"));
Actions builder = new Actions(driver);
builder.MoveToElement(target , -375 , -436).Click().Build().Perform();
I got the -375, -436 from clicking on an element and then dragging backwards until I reached the point I needed to click. The coordinates that MeasureIT said I just subtracted. In my example above, the only element I had on the page that was clickable was the "loginAsEu" link. So I started from there.
If all other methods mentioned on this page failed and you are using python, I suggest you use the mouse module to do it in a more native way instead. The code would be as simple as
import mouse
mouse.move("547", "508")
mouse.click(button='left')
You can also use the keyboard module to simulate keyboard actions
import keyboard
keyboard.write('The quick brown fox jumps over the lazy dog.')
To find the coordinate of the mouse, you can use the follow JavaScript Code.
document.onclick=function(event) {
var x = event.screenX ;
var y = event.screenY;
console.log(x, y)
}
If you don't like screenX, you can use pageX or clientX. More on here
P.S. I come across a website that prevents programmatic interactions with JavaScript/DOM/Selenium. This is probably a robot prevention mechanism. However, there is no way for them to ban OS actions.
WebElement button = driver.findElement(By.xpath("//button[#type='submit']"));
int height = button.getSize().getHeight();
int width = button.getSize().getWidth();
Actions act = new Actions(driver);
act.moveToElement(button).moveByOffset((width/2)+2,(height/2)+2).click();
import pyautogui
from selenium import webdriver
driver = webdriver.Chrome(chrome_options=options)
driver.maximize_window() #maximize the browser window
driver.implicitly_wait(30)
driver.get(url)
height=driver.get_window_size()['height']
#get browser navigation panel height
browser_navigation_panel_height = driver.execute_script('return window.outerHeight - window.innerHeight;')
act_y=y%height
scroll_Y=y/height
#scroll down page until y_off is visible
try:
driver.execute_script("window.scrollTo(0, "+str(scroll_Y*height)+")")
except Exception as e:
print "Exception"
#pyautogui used to generate click by passing x,y coordinates
pyautogui.FAILSAFE=False
pyautogui.moveTo(x,act_y+browser_navigation_panel_height)
pyautogui.click(x,act_y+browser_navigation_panel_height,clicks=1,interval=0.0,button="left")
This is worked for me. Hope, It will work for you guys :)...
I used AutoIt to do it.
using AutoIt;
AutoItX.MouseClick("LEFT",150,150,1,0);//1: click once, 0: Move instantaneous
Pro:
simple
regardless of mouse movement
Con:
since coordinate is screen-based, there should be some caution if the app scales.
the drive won't know when the app finish with clicking consequence actions. There should be a waiting period.
To add to this because I was struggling with this for a while. These are the steps I took:
Find the coordinates you need to click. Use the code below in your console and it will display and alert of the coordinates you need.
document.onclick = function(e)
{
var x = e.pageX;
var y = e.pageY;
alert("User clicked at position (" + x + "," + y + ")")
};
Make sure the element is actually visible on the screen and click it using Actionchains
WebDriverWait(DRIVER GOES HERE, 10).until(EC.element_to_be_clickable(YOUR ELEMENT LOCATOR GOES HERE))
actions = ActionChains(DRIVER GOES HERE)
actions.move_by_offset(X, Y).click().perform()
Selenium::WebDriver has PointerActions module which includes a number of actions you can chain and/or trigger without specifying the element, eg. move_to_location or move_by. See detailed specs here. As you can see, you can only use actual coordinates. I am sure this interface is implemented in other languages / libraries accordingly.
My short reply may echo some other comments here, but I just wanted to provide a link for reference.
You could use the html tag as the element and then use the coordinates you want, in this example below I am using Javascript. But I am able to click on the top left of the screen.
async function clickOnTopLeft() {
const html = driver.wait(
until.elementLocated(By.xpath('/html')),
10000)
const { width, height } = await html.getRect()
const offSetX = - Math.floor(width / 2)
const offsetY = - Math.floor(height / 2)
await driver.actions().move(
{ origin: html, x: offSetX, y: offsetY })
.click().perform()
}
If you can see the source code of page, its always the best option to refer to the button by its id or NAME attribute. For example you have button "Login" looking like this:
<input type="submit" name="login" id="login" />
In that case is best way to do
selenium.click(id="login");
Just out of the curiosity - isnt that HTTP basic authentification? In that case maybe look at this:
http://code.google.com/p/selenium/issues/detail?id=34
Selenium won't let you do this.