Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update element.py: box_model, size, location, rect, get_attribute, is_displayed, is_enabled, is_selected, is_clickable #13

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions nodriver/core/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,18 @@ async def send_keys(self, text: str):
await self._tab.send(cdp.input_.dispatch_key_event("char", text=char))
for char in list(text)
]

async def write(self, text: str):
"""
write text to an input field, or any other html element.

:param text: text to write
:return: None
"""
await self.apply("(elem) => elem.focus()")
[
await self._tab.send(cdp.input_.insert_text(text=text))
]

async def send_file(self, *file_paths: PathLike):
"""
Expand Down Expand Up @@ -798,6 +810,123 @@ def text_all(self):
text_nodes = util.filter_recurse_all(self.node, lambda n: n.node_type == 3)
return " ".join([n.node_value for n in text_nodes])

async def box_model(self) -> cdp.dom.BoxModel:
"""The box model of the element.

:return: The box model of the element.
:rtype: cdp.dom.BoxModel, but can return None if the box model could not be retrieved.
"""

model_box = await self.tab.send(cdp.dom.get_box_model(backend_node_id=self.backend_node_id))
return model_box

async def size(self) -> dict:
"""The size of the element.

:return: The size of the element.
:rtype: dict
"""
box_model = await self.box_model()
return {"height": 0, "width": 0} if box_model is None else {"height": box_model.height, "width": box_model.width}

async def location(self) -> dict:
"""The location of the element in the renderable canvas.

:return: The location of the element in the renderable canvas.
:rtype: dict
"""
result = await self.box_model()

# Return 0, 0 if the result is None
if result is None:
return {"x": 0, "y": 0}

# The box model is a list of 4 points, we need to find the top-left point
top_left_x = result.content[0]
top_left_y = result.content[1]

# Return the result as a dictionary
return {"x": top_left_x, "y": top_left_y}

async def rect(self) -> dict:
"""A dictionary with the size and location of the element.

:return: A dictionary with the size and location of the element.
:rtype: dict
"""
result = await self.box_model()

# Return 0, 0 if the result is None
if result is None:
return {"top_left": {"x": 0, "y": 0}, "bottom_right": {"x": 0, "y": 0}, "width": 0, "height": 0}

# The box model is a list of 4 points, we need to find the top-left and bottom-right points
top_left_x = result.content[0]
top_left_y = result.content[1]
bottom_right_x = result.content[4]
bottom_right_y = result.content[5]

# Return the result as a dictionary
return {
"top_left": {"x": top_left_x, "y": top_left_y},
"bottom_right": {"x": bottom_right_x, "y": bottom_right_y},
"width": result.width,
"height": result.height
}

async def get_attribute(self, name: str) -> typing.Optional[str]:
"""Gets the given attribute or property of the element.

:param name: The name of the attribute or property to retrieve.
:return: The value of the attribute or property.
"""
return self.attrs.get(name)

async def is_displayed(self):
"""
checks if the element is displayed on the page

checks if the width and height are > 0
:return: True if the element is displayed, False otherwise.
:rtype: bool
"""
size = await self.size()
return (size["height"] > 0 and size["width"] > 0)

async def is_enabled(self):
"""
checks if the element is enabled

checks if the element is not disabled

:return: True if the element is enabled, False otherwise.
:rtype: bool
"""

return not bool(await self.get_attribute("disabled"))

async def is_selected(self):
"""
checks if the element is selected

checks if the element is checked

:return: True if the element is selected, False otherwise.
:rtype: bool
"""

return bool(await self.get_attribute("checked"))

async def is_clickable(self):
"""
checks if the element is clickable

checks if the element is displayed and enabled
:return: True if the element is clickable, False otherwise.
:rtype: bool
"""
return await self.is_displayed() and await self.is_enabled()

async def query_selector_all(self, selector: str):
"""
like js querySelectorAll()
Expand Down